Project restructure

This commit is contained in:
2025-03-20 16:31:48 -04:00
parent bbffe86d53
commit f2bb31af8b
19 changed files with 1887 additions and 67 deletions

1592
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,6 @@
[package]
name = "pokemon_analysis"
version = "0.1.0"
edition = "2024"
[dependencies]
[lib]
name = "pokemon_lib"
path = "src/lib.rs"
[[bin]]
name = "demo"
path = "src/main.rs"
[workspace]
resolver = "2"
members = [
"pokedex",
"pokedex-viewer"
]

View File

@@ -0,0 +1,69 @@
# The Pokemon.json File Format
The Pokemon.json file (and potential other future JSON files) serve as the actual initial data the pokedex uses when
constructing all the data structures at startup. The layout of this file is described below. For the most part, the file
is not hand-authored, and instead most of the data is either directly retrieved or derived from PokeAPI data. As
explained in the [Rationale](#Rationale) section however, the data is by no means bound to only contain (or explicitly
match) the data found in PokeAPI.
## Rationale
Although currently all application data for the pokedex comes courtesy of pokeapi, I've decided to make our own custom
JSON format for the data used by the pokedex. The data in this file still primarily comes from PokeAPI, but undergoes
a conversion process to match the expected format. Right now, this is done manually, but in the future there will be a
conversion process that can do this automatically when changes to the PokeAPI data are detected. Using our own file and
custom format accomplishes a number of goals:
1. Independence from changes to the PokeAPI JSON format. In case the format for PokeAPI ever changes, or even in the
case that there may be an error in the format, we do not need to immediately change anything about our own data.
2. More convenient data format layout. By using our own custom format, we are free to adapt it in the future to more
closely match what the code actually works with, easing the development process for new features or working on bugs.
3. No need to interface with pokeapi directly. By the very nature of this being a pokedex application, it will require
almost all the data related to pokemon species that PokeAPI provides. While PokeAPI encourages cacheing on request, in
the case of our application it will almost certainly be better if we just bundle all the data for the app rather than
potentially make many requests to PokeAPI. Likewise, when the app checks for an update the data can come from the
github repository or a separate server we manage, or even a separate data source the end user provides in the future.
4. Ability to add our own data, or data from other sources, without having to explicitly add more application code. A
format we control means that in the future, we can simply add any additional data we desire to the files, or even
pull data from sources other than PokeAPI.
## Format
The file contains a top-level JSON object which contains a little metadata about all the pokemon it contains. This object
primarily contains the array of PokemonSpecies objects.
### Pokedex Root Object Layout
The fields of the top-level object are as follows:
- version: A semver string denoting the current version of the data.
- update: An additional integer that can be considered a part of the version. It denotes the specific build number for
this version. This value is incremented anytime the data in the file is updated. The application will primarily use
this and the version string to determine if this version of the file is newer or older than the version it currently
uses.
- pokemon_count: A non-zero integer value for the number of unique PokemonSpecies entries in the "pokemon" list.
- pokemon: An array of PokemonSpecies objects.
### PokemonSpecies Object Layout
The following fields are defined for PokemonSpecies:
- id - The national dex id number associated with this species.
- name - The primary name used for the species.
- generation - A list of generations this species is available in.
- type1 - The primary Type associated with this species.
- type2 - The secondary Type associated with this species.
- height - A scaled integer representing the height of the pokemon in meters. The last two digits are the decimal
fraction component of the height, so the height can be considered as being scaled by a factor of 100.
- weight - A scaled integer representing the weight of the pokemon in kilograms. The last two digits are the decimal
fraction component of the weight, so the weight can be considered as being scaled by a factor of 100.
```json
{
id: number,
name:
}
```
[
]

11
pokedex-viewer/Cargo.toml Normal file
View File

@@ -0,0 +1,11 @@
[package]
name = "pokedex-viewer"
version = "0.1.0"
edition = "2024"
[dependencies]
pokedex = { path = "../pokedex" }
[[bin]]
name = "pokedex-viewer"
path = "src/main.rs"

View File

@@ -0,0 +1,3 @@
fn main() {
println!("Hello, World!");
}

11
pokedex/Cargo.toml Normal file
View File

@@ -0,0 +1,11 @@
[package]
name = "pokedex"
version = "0.1.0"
edition = "2024"
[lib]
name = "pokedex"
path = "src/lib.rs"
[dependencies]
reqwest = { version = "0.12.14", features = ["blocking"] }

5
pokedex/src/lib.rs Normal file
View File

@@ -0,0 +1,5 @@
#![allow(dead_code)]
pub mod types;
pub mod resources;
pub mod pokemon;

View File

@@ -0,0 +1,49 @@
use super::{item::Item, moves::PokemonMove, PokemonGender, PokemonSpecies};
/// An enum for the various days/times/blocks that are relevant for various pokemon, items, etc.
/// Date and Time in pokemon games can get complicated since every game has differences in how it
/// handles date and time, how precisely date and time need to be represented, and how closely date
/// and time map to the real world. This is an incomplete, but hopefully close enough,
/// approximation.
///
/// As most events in pokemon don't tend to care about the precise minute, they can usually be
/// broken down into "during the day", "during the night", "between 6pm and 9pm", "on a Friday",
/// etc. This enum can be combined multiple times to get something more precise like "Between 6pm
/// and 12pm on a Friday".
#[derive(Debug, Copy, Clone)]
pub enum PokemonTime {
Daytime,
Nighttime,
DayOfWeek(u8),
BetweenHours(u8, u8),
BetweenMinutes(u8, u8),
}
/// Simple enum for the different restrictions on evolution
#[derive(Debug, Clone)]
pub enum EvolutionRequirement {
SpecificTime(PokemonTime),
CurrentlyInArea(String),
Friendship(u8),
HoldItem(Item),
KnowMove(PokemonMove),
Gender(PokemonGender),
Other(String),
}
/// Simple enum for the different methods of evolution
#[derive(Debug, Clone, Default)]
pub enum EvolutionMethod {
#[default]
LevelUp,
UseItem(Item),
Trade,
}
#[derive(Debug, Clone)]
pub struct Evolution {
from: PokemonSpecies,
to: PokemonSpecies,
method: EvolutionMethod,
}

View File

@@ -0,0 +1,3 @@
/// Stub newtype for Items.
#[derive(Debug, Clone, Default)]
pub struct Item(String);

View File

@@ -0,0 +1,13 @@
mod species;
pub mod evolution;
pub mod item;
pub mod moves;
pub use species::*;
/// Stub enum for gender
#[derive(Debug, Copy, Clone)]
pub enum PokemonGender {
Male,
Female,
}

View File

@@ -0,0 +1,3 @@
/// Stub newtype for pokemon moves.
#[derive(Debug, Clone, Default)]
pub struct PokemonMove(String);

View File

@@ -0,0 +1,86 @@
use crate::types::Type;
use super::evolution::Evolution;
const DEFAULT_CAPACITY: usize = 2048;
/// Simple newtype for Pokemon Species Index.
#[derive(Debug, Copy, Clone, Default)]
pub struct PokemonSpecies(u32);
/// Simple type for pokemon typing.
#[derive(Debug, Copy, Clone, Default)]
pub struct Typing {
primary: Type,
secondary: Option<Type>,
}
/// Simple newtype for height and weight, which are scaled integers
#[derive(Debug, Copy, Clone, Default)]
pub struct ScaledInteger(u32);
/// Simple newtype for abilities. Temporary.
#[derive(Debug, Clone, Default)]
pub struct Ability(String);
#[derive(Debug, Copy, Clone, Default)]
pub struct BaseStats {
pub hp: u8,
pub attack: u8,
pub defense: u8,
pub sp_attack: u8,
pub sp_defense: u8,
pub speed: u8,
}
impl BaseStats {
pub fn total(&self) -> u16 {
(self.hp as u16)
+ (self.attack as u16)
+ (self.defense as u16)
+ (self.sp_attack as u16)
+ (self.sp_defense as u16)
+ (self.speed as u16)
}
}
pub struct MiscSpeciesData {
species_descriptor: String,
height: ScaledInteger,
weight: ScaledInteger,
}
/// This structure contains all the data necessary for the context of the application as it relates to the pokemon the
/// application uses. It acts as a "context" which contains all the data for pokemon in an arbitrary pokedex. Usually
/// this is used for the "national dex" which contains data for all pokemon of all generations, but it can be
/// arbitrarily built as well.
#[derive(Debug, Clone)]
pub struct Pokedex {
/*
* Implementation Details:
*
* This struct is a "Struct of Arrays (SoA)" as a manner of speaking. In theory, we could have
* an Array of Structs (AoS) of Pokemon Species, but it is not generally common that a user of
* the pokedex is interested in ALL the data for a given species. Usually, they want to do
* something like iterate over base stats, abilities, egg groups, or evolution lines. Therefore,
* it makes more sense for a "Species" to simply be an index that can be used to lookup the
* relevant data, instead of stuffing all the data into a single Species struct.
*/
species: Vec<PokemonSpecies>,
base_stats: Vec<BaseStats>,
types: Vec<(Type, Option<Type>)>,
abilities: Vec<(Ability, Option<Ability>, Option<Ability>)>,
evolutions: Vec<Option<Evolution>>,
}
impl Default for Pokedex {
fn default() -> Self {
Self {
species: Vec::with_capacity(DEFAULT_CAPACITY),
base_stats: Vec::with_capacity(DEFAULT_CAPACITY),
types: Vec::with_capacity(DEFAULT_CAPACITY),
abilities: Vec::with_capacity(DEFAULT_CAPACITY),
evolutions: Vec::with_capacity(DEFAULT_CAPACITY),
}
}
}

View File

@@ -0,0 +1,4 @@
mod pokeapi;
pub mod pokemon;
pub use pokeapi::*;

View File

@@ -0,0 +1,18 @@
/// A struct for interfacing with the PokeAPI REST API.
pub struct PokeAPIClient {
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum APIError {
ResourceNotFound
}
impl PokeAPIClient {
pub fn init() -> Self {
Self {}
}
pub fn retrieve_pokemon_by_id(_id: usize) {
}
}

View File

@@ -0,0 +1,2 @@
struct PokemonJSON {
}

View File

@@ -0,0 +1,6 @@
use crate::pokemon::PokemonSpecies;
/// Retrieves info about a given pokemon species given its national dex id
pub fn get_species_by_id(_id: usize) -> PokemonSpecies {
todo!()
}

View File

@@ -1,11 +1,12 @@
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)]
pub enum SpecialType {
Bird, // Only present in gens 1 and 2 as an unused type value.
#[default]
Unknown, // Only present in older generations
Bird, // Only present in gens 1 and 2 as an unused type value.
Stellar, // Only present in gen 9 for the tera stellar type.
}
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Default, PartialEq, Hash)]
pub enum Type {
Bug,
Dark,
@@ -19,7 +20,10 @@ pub enum Type {
Grass,
Ground,
Ice,
#[default]
Normal,
Poison,
Psychic,
Rock,

View File

@@ -1,2 +0,0 @@
mod types;
pub use types::*;

View File

@@ -1,47 +0,0 @@
use pokemon_lib::*;
fn main() {
let mut total = 0u32;
let mut neutral = 0u32;
let mut immune = 0u32;
let mut effective = 0u32;
let mut resisted = 0u32;
let mut double_effective = 0u32;
let mut double_resisted = 0u32;
for t1 in TYPES {
for t2 in TYPES {
for t3 in TYPES {
total += 1;
let effectiveness = Type::double_effectiveness(&t1, &t2, &t3);
match effectiveness {
DoubleEffectiveness::Immune => immune += 1,
DoubleEffectiveness::Neutral => neutral += 1,
DoubleEffectiveness::Effective => effective += 1,
DoubleEffectiveness::Resisted => resisted += 1,
DoubleEffectiveness::DoubleEffective => double_effective += 1,
DoubleEffectiveness::DoubleResisted => double_resisted += 1,
}
}
}
}
let mut percent: f64;
println!("TOTALS: ");
println!("TOTAL: {total}");
percent = 100.0 * neutral as f64 / total as f64;
println!("NEUTRAL: {neutral} ({percent:.2}%)");
percent = 100.0 * immune as f64 / total as f64;
println!("IMMUNE: {immune} ({percent:.2}%)");
percent = 100.0 * effective as f64 / total as f64;
println!("EFFECTIVE: {effective} ({percent:.2}%)");
percent = 100.0 * resisted as f64 / total as f64;
println!("RESISTED: {resisted} ({percent:.2}%)");
percent = 100.0 * double_effective as f64 / total as f64;
println!("DOUBLE_EFFECTIVE: {double_effective} ({percent:.2}%)");
percent = 100.0 * double_resisted as f64 / total as f64;
println!("DOUBLE_RESISTED: {double_resisted} ({percent:.2}%)");
}