hardcode stuff
This commit is contained in:
parent
3da437c4ba
commit
48cc893d42
4 changed files with 144 additions and 143 deletions
31
Cargo.lock
generated
31
Cargo.lock
generated
|
@ -134,12 +134,6 @@ version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "color_quant"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
@ -186,16 +180,6 @@ dependencies = [
|
||||||
"windows-targets",
|
"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]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -210,8 +194,6 @@ checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder-lite",
|
"byteorder-lite",
|
||||||
"color_quant",
|
|
||||||
"gif",
|
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"png",
|
"png",
|
||||||
]
|
]
|
||||||
|
@ -228,12 +210,6 @@ version = "1.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy_static"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.171"
|
version = "0.2.171"
|
||||||
|
@ -290,7 +266,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"image",
|
"image",
|
||||||
"lazy_static",
|
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -435,12 +410,6 @@ dependencies = [
|
||||||
"wit-bindgen-rt",
|
"wit-bindgen-rt",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "weezl"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
|
|
|
@ -5,8 +5,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.32", features = ["derive"] }
|
clap = { version = "4.5.32", features = ["derive"] }
|
||||||
image = { version = "0.25.5", default-features = false, features = ["png", "gif"] }
|
image = { version = "0.25.5", default-features = false, features = ["png"] }
|
||||||
lazy_static = "1.5.0"
|
|
||||||
rand = "0.9.0"
|
rand = "0.9.0"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
|
|
25
gen_efficacy.rb
Normal file
25
gen_efficacy.rb
Normal file
|
@ -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 ")}
|
||||||
|
}"
|
228
src/main.rs
228
src/main.rs
|
@ -1,23 +1,14 @@
|
||||||
#![allow(
|
#![allow(
|
||||||
clippy::cast_sign_loss,
|
clippy::cast_sign_loss,
|
||||||
clippy::cast_possible_wrap,
|
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
|
use std::{fmt, process};
|
||||||
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 clap::Parser;
|
use clap::Parser;
|
||||||
use image::{ImageBuffer, Rgb};
|
use image::{ImageBuffer, Rgb};
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use rand::{
|
use rand::{
|
||||||
distr::{Distribution, StandardUniform},
|
distr::{Distribution, StandardUniform},
|
||||||
Rng,
|
Rng,
|
||||||
|
@ -36,39 +27,21 @@ fn main() {
|
||||||
} = Args::parse();
|
} = Args::parse();
|
||||||
|
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
|
|
||||||
let mut game = Game::new_random(width, height, &mut rng);
|
let mut game = Game::new_random(width, height, &mut rng);
|
||||||
|
let mut last = game.clone();
|
||||||
for i in 0..steps {
|
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");
|
let path = format!("{output_prefix}{i:0OUTPUT_DIGITS$}.png");
|
||||||
|
// TODO: reuse `ImageBuffer`
|
||||||
if let Err(e) = ImageBuffer::from(&game).save(path) {
|
if let Err(e) = ImageBuffer::from(&game).save(path) {
|
||||||
eprintln!("error saving image: {e}");
|
eprintln!("error saving image: {e}");
|
||||||
process::exit(1);
|
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)]
|
#[derive(Debug, clap::Parser)]
|
||||||
|
@ -93,6 +66,12 @@ struct Game {
|
||||||
field: Vec<Type>,
|
field: Vec<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct GameStep {
|
||||||
|
old: Game,
|
||||||
|
new: Game,
|
||||||
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
fn new_random(width: u16, height: u16, rng: &mut impl Rng) -> Self {
|
fn new_random(width: u16, height: u16, rng: &mut impl Rng) -> Self {
|
||||||
let (width, height) = (i64::from(width), i64::from(height));
|
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
|
// let's see if we can get away with not swapping the field
|
||||||
// nah it's easy
|
// nah it's easy
|
||||||
fn step(&mut self, rng: &mut impl Rng) {
|
fn step(self, mut next: Self, rng: &mut impl Rng) -> GameStep {
|
||||||
let mut next = self.clone();
|
|
||||||
for x in 0..self.width {
|
for x in 0..self.width {
|
||||||
for y in 0..self.height {
|
for y in 0..self.height {
|
||||||
let dx = rng.random_range(-1..=1);
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
enum Efficacy {
|
enum Efficacy {
|
||||||
Super,
|
Strong,
|
||||||
Neutral,
|
Neutral,
|
||||||
NotVery,
|
Weak,
|
||||||
Immune,
|
Immune,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,59 +192,119 @@ impl Type {
|
||||||
Self::Fairy,
|
Self::Fairy,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// https://gist.github.com/apaleslimghost/0d25ec801ca4fc43317bcff298af43c3
|
||||||
fn color(self) -> Rgb<u8> {
|
fn color(self) -> Rgb<u8> {
|
||||||
TYPE_COLORS[&self]
|
use Type::*;
|
||||||
}
|
match self {
|
||||||
|
Normal => Rgb([0xA8, 0xA7, 0x7A]),
|
||||||
fn advantage(self) -> &'static TypeAdvantage {
|
Fire => Rgb([0xEE, 0x81, 0x30]),
|
||||||
&TYPE_ADVANTAGES[&self]
|
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
|
// nothing is immune to itself, phew
|
||||||
fn win_chance(self, other: Type) -> f32 {
|
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 self_eff = self.efficacy(other);
|
||||||
let def_eff = other.efficacy(self);
|
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 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
|
// if you are four times as effective, you should win four times as often: 4/5 vs 1/5
|
||||||
match (self_eff, def_eff) {
|
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, _) => 1.0,
|
||||||
(_, Immune) => 0.0,
|
(_, Immune) => 0.0,
|
||||||
(Efficacy::Super, Efficacy::Neutral) | (Efficacy::Neutral, Efficacy::NotVery) => {
|
(Efficacy::Strong, Efficacy::Neutral) | (Efficacy::Neutral, Efficacy::Weak) => 2. / 3.,
|
||||||
2. / 3.
|
(Efficacy::Neutral, Efficacy::Strong) | (Efficacy::Weak, Efficacy::Neutral) => 1. / 3.,
|
||||||
}
|
(Efficacy::Strong, Efficacy::Weak) => 4. / 5.,
|
||||||
(Efficacy::Neutral, Efficacy::Super) | (Efficacy::NotVery, Efficacy::Neutral) => {
|
(Efficacy::Weak, Efficacy::Strong) => 1. / 5.,
|
||||||
1. / 3.
|
|
||||||
}
|
|
||||||
(Efficacy::Super, Efficacy::NotVery) => 4. / 5.,
|
|
||||||
(Efficacy::NotVery, Efficacy::Super) => 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 {
|
fn efficacy(self, defender: Type) -> Efficacy {
|
||||||
if defender.is_immune_to(self) {
|
use Efficacy::*;
|
||||||
return Efficacy::Immune;
|
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:?}")
|
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue