From df8d4d06bf2567f8166edfa1754100a109fe41d9 Mon Sep 17 00:00:00 2001
From: mehbark <terezi@pyrope.net>
Date: Tue, 11 Mar 2025 20:50:18 -0400
Subject: [PATCH] tweaks

---
 gen_efficacy.rb |  4 +--
 src/main.rs     | 96 ++++++++++++++++++++++++++++---------------------
 2 files changed, 57 insertions(+), 43 deletions(-)

diff --git a/gen_efficacy.rb b/gen_efficacy.rb
index 5480e18..34e7ebd 100644
--- a/gen_efficacy.rb
+++ b/gen_efficacy.rb
@@ -13,13 +13,13 @@ end
 branches = eff.flat_map do |eff|
   type = eff['name']
   [
-    *make_branches(type, eff['immunes'], 'Immune'),
+    *make_branches(type, eff['immunes'], 'Zero'),
     *make_branches(type, eff['weaknesses'], 'Weak'),
     *make_branches(type, eff['strengths'], 'Strong'),
-    *make_branches(type, ['_'], 'Neutral'),
   ]
 end
 
 puts "match (self, defender) {
   #{branches.join("\n  ")}
+  #{make_branches('_', ['_'], 'Neutral')[0]}
 }"
diff --git a/src/main.rs b/src/main.rs
index b554087..432bdf0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,7 +11,7 @@ use clap::Parser;
 use image::{ImageBuffer, Rgb};
 use rand::{
     distr::{Distribution, StandardUniform},
-    Rng,
+    Rng, RngCore,
 };
 use serde::Deserialize;
 
@@ -33,17 +33,22 @@ fn main() {
 
     let mut buffer = ImageBuffer::new(u32::from(width), u32::from(height));
 
+    let mut rolls = vec![0; width as usize * height as usize];
+
+    let step_digits = format!("{steps}").len();
     for i in 0..steps {
-        let GameStep { old, new } = game.step(last, &mut rng);
+        rng.fill_bytes(&mut rolls);
+        let GameStep { old, new } = game.step(last, &mut rng, &rolls);
         game = new;
         last = old;
-
         game.write_to_buffer(&mut buffer);
+
         let path = format!("{output_prefix}{i:0OUTPUT_DIGITS$}.png");
         if let Err(e) = buffer.save(path) {
             eprintln!("error saving image: {e}");
             process::exit(1);
         }
+        eprintln!("step {:step_digits$}/{steps}", i + 1);
     }
 }
 
@@ -105,7 +110,7 @@ impl Game {
 
     // let's see if we can get away with not swapping the field
     // nah it's easy
-    fn step(self, mut next: Self, rng: &mut impl Rng) -> GameStep {
+    fn step(self, mut next: Self, rng: &mut impl Rng, rolls: &[u8]) -> GameStep {
         for x in 0..self.width {
             for y in 0..self.height {
                 let dx = rng.random_range(-1..=1);
@@ -126,7 +131,8 @@ impl Game {
                     continue;
                 }
 
-                let won = rng.random::<f32>() <= here.win_chance(other);
+                let roll = rolls[x as usize + y as usize * self.width as usize];
+                let won = roll <= here.win_chance(other);
                 if won {
                     next.set(ox, oy, here);
                 } else {
@@ -173,12 +179,37 @@ enum Type {
     Fairy,
 }
 
+impl From<Type> for usize {
+    fn from(value: Type) -> Self {
+        match value {
+            Type::Normal => 0,
+            Type::Fire => 1,
+            Type::Water => 2,
+            Type::Electric => todo!(),
+            Type::Grass => todo!(),
+            Type::Ice => todo!(),
+            Type::Fighting => todo!(),
+            Type::Poison => todo!(),
+            Type::Ground => todo!(),
+            Type::Flying => todo!(),
+            Type::Psychic => todo!(),
+            Type::Bug => todo!(),
+            Type::Rock => todo!(),
+            Type::Ghost => todo!(),
+            Type::Dragon => todo!(),
+            Type::Dark => todo!(),
+            Type::Steel => todo!(),
+            Type::Fairy => todo!(),
+        }
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 enum Efficacy {
     Strong,
     Neutral,
     Weak,
-    Immune,
+    Zero,
 }
 
 impl Type {
@@ -229,20 +260,20 @@ impl Type {
     }
 
     // nothing is immune to itself, phew
-    fn win_chance(self, other: Type) -> f32 {
-        use Efficacy::{Immune, Neutral, Strong, Weak};
+    fn win_chance(self, other: Type) -> u8 {
+        use Efficacy::{Neutral, Strong, Weak, Zero};
         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) {
-            (Strong, Strong) | (Neutral, Neutral) | (Weak, Weak) | (Immune, Immune) => 0.5,
-            (Immune, _) => 1.0,
-            (_, Immune) => 0.0,
-            (Strong, Neutral) | (Neutral, Weak) => 2. / 3.,
-            (Neutral, Strong) | (Weak, Neutral) => 1. / 3.,
-            (Strong, Weak) => 4. / 5.,
-            (Weak, Strong) => 1. / 5.,
+            (Strong, Strong) | (Neutral, Neutral) | (Weak, Weak) | (Zero, Zero) => u8::MAX / 2,
+            (Zero, _) => 0,
+            (_, Zero) => u8::MAX,
+            (Strong, Neutral) | (Neutral, Weak) => 170, // 2/3
+            (Neutral, Strong) | (Weak, Neutral) => 85,  // 1/3
+            (Strong, Weak) => 204,                      // 4/5
+            (Weak, Strong) => 51,                       // 1/5
         }
     }
 
@@ -254,67 +285,50 @@ impl Type {
         use Efficacy::*;
         use Type::*;
         match (self, defender) {
-            (Normal, Ghost) => Immune,
+            (Normal, Ghost) => Zero,
             (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, Ground) => Zero,
             (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, Ghost) => Zero,
             (Fighting, Poison | Flying | Psychic | Bug | Fairy) => Weak,
             (Fighting, Normal | Ice | Rock | Dark | Steel) => Strong,
-            (Fighting, _) => Neutral,
-            (Poison, Steel) => Immune,
+            (Poison, Steel) => Zero,
             (Poison, Poison | Ground | Rock | Ghost) => Weak,
             (Poison, Grass | Fairy) => Strong,
-            (Poison, _) => Neutral,
-            (Ground, Flying) => Immune,
+            (Ground, Flying) => Zero,
             (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, Dark) => Zero,
             (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, Normal) => Zero,
             (Ghost, Dark) => Weak,
             (Ghost, Psychic | Ghost) => Strong,
-            (Ghost, _) => Neutral,
-            (Dragon, Fairy) => Immune,
+            (Dragon, Fairy) => Zero,
             (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,
+            (_, _) => Neutral,
         }
     }
 }