16 releases (4 breaking)

0.5.0 Oct 14, 2023
0.4.12 May 28, 2023
0.4.5 Apr 25, 2023
0.4.4 Mar 12, 2023
0.1.0 Feb 9, 2023

#112 in Testing

Download history 1/week @ 2024-02-21 21/week @ 2024-02-28 132/week @ 2024-03-06

153 downloads per month

MIT license

1MB
24K SLoC

SAPTest

CI codecov

Database and testing framework for Super Auto Pets.

Game information is queried from the Super Auto Pets wiki page and stored in a SQLite database.


Usage

Add it to a project with cargo.

cargo add saptest

Teams

Build a Team and simulate battles between them.

Then visualize the results in .dot format!

use saptest::{
    Pet, PetName, Food, FoodName,
    Team, TeamCombat, Position, create_battle_digraph
};
// Create a team.
let mut team = Team::new(
    &vec![Some(Pet::try_from(PetName::Ant).unwrap()); 5],
    5
).unwrap();
let mut enemy_team = team.clone();

// Set a seed for a team.
team.set_seed(Some(25));

// Give food to pets.
team.set_item(&Position::First, Food::try_from(FoodName::Garlic).ok()).unwrap();
enemy_team.set_item(&Position::First, Food::try_from(FoodName::Garlic).ok()).unwrap();

// And fight!
team.fight(&mut enemy_team).unwrap();

// Create a graph of the fight.
println!("{}", create_battle_digraph(&team, false));
digraph {
    rankdir=LR
    node [shape=box, style="rounded, filled", fontname="Arial"]
    edge [fontname="Arial"]
    0 [ label = "Ant_0 - The Fragile Truckers_copy" ]
    1 [ label = "Ant_0 - The Fragile Truckers", fillcolor = "yellow" ]
    2 [ label = "Ant_3 - The Fragile Truckers", fillcolor = "yellow" ]
    3 [ label = "Ant_4 - The Fragile Truckers_copy" ]
    0 -> 1 [ label = "(Attack, Damage (0, 2), Phase: 1)" ]
    1 -> 0 [ label = "(Attack, Damage (0, 2), Phase: 1)" ]
    1 -> 2 [ label = "(Faint, Add (1, 1), Phase: 1)" ]
    0 -> 3 [ label = "(Faint, Add (1, 1), Phase: 1)" ]
}

Graphs can be as simple as the example above... or extremely complex.

Using Graphviz Online.

Shops

Add shop functionality to a Team and roll, freeze, buy/sell pets and foods.

use saptest::{
    Shop, ShopItem, TeamShopping, Team,
    Position, Entity, EntityName, Food, FoodName,
    db::pack::Pack
};

// All teams are constructed with a shop at tier 1.
let mut team = Team::default();

// All shop functionality is supported.
team.set_shop_seed(Some(1212))
    .set_shop_packs(&[Pack::Turtle])
    .open_shop().unwrap()
    .buy(
        &Position::First, // From first.
        &Entity::Pet, // Pet
        &Position::First // To first position, merging if possible.
    ).unwrap()
    .move_pets(
        &Position::First, // First pet.
        &Position::Relative(-2), // To pet 2 slots behind. Otherwise, ignore.
        true // And merge them if possible.
    ).unwrap()
    .sell(&Position::First).unwrap()
    .freeze_shop(&Position::Last, &Entity::Pet).unwrap()
    .roll_shop().unwrap()
    .close_shop().unwrap();

// Shops can be built separately and can replace a team's shop.
let mut tier_5_shop = Shop::new(3, Some(42)).unwrap();
let weakness = ShopItem::new(
    Food::try_from(FoodName::Weak).unwrap()
);
tier_5_shop.add_item(weakness).unwrap();
team.replace_shop(tier_5_shop).unwrap();

Pets

Build custom Pets and Effects.

use saptest::{
    Pet, PetName, PetCombat,
    Food, FoodName,
    Entity, Position, Effect, Statistics,
    effects::{
        trigger::TRIGGER_START_BATTLE,
        actions::GainType,
        state::Target,
        actions::Action
    }
};
// Create known pets.
let mut pet = Pet::try_from(PetName::Ant).unwrap();

// Or custom pets and effects.
let custom_effect = Effect::new(
    TRIGGER_START_BATTLE, // Effect trigger
    Target::Friend, // Target
    Position::Adjacent, // Positions
    Action::Gain(GainType::DefaultItem(FoodName::Melon)), // Action
    Some(1), // Number of uses.
    false, // Is temporary.
);
let mut custom_pet = Pet::custom(
    "MelonBear",
    Statistics::new(50, 50).unwrap(),
    &[custom_effect],
);

// Fight two pets individually as well.
// Note: Effects don't activate here.
pet.attack(&mut custom_pet);

Logging

Enable logging with a crate like simple_logger.

Config

To configure the global SapDB's startup, create a .saptest.toml file in the root of your project.

  • Specify page versions for pets, foods, and tokens to query.
  • Toggle recurring updates on startup.
  • Set database filename.
[database]
# https://superautopets.wiki.gg/index.php?title=Pets&oldid=4634
pets_version = 4634
filename = "./sap.db"
update_on_startup = false

Graph building can also be disabled in [general] with build_graph.

[general]
build_graph = false

Benchmarks

Benchmarks for saptest are located in benches/battle_benchmarks.rs and run using the criterion crate.

  • Compared against sapai, a Super Auto Pets testing framework written in Python.
  • Both tests were run on an AMD Ryzen 5 5600 6-Core Processor @ 3.50 GHz.
# saptest
git clone git@github.com:koisland/SuperAutoTest.git --depth 1
cargo add cargo-tarpaulin
cargo bench && open target/criterion/sapai_example/report/index.html
# sapai
cd benches/
git clone https://github.com/manny405/sapai.git && cd sapai
python setup.py install
# Then run `battle_benchmarks_sapai.ipynb`.

saptest (No Graphs)

  • TODO

saptest (Graphs)

  • TODO

sapai

  • 4.29 ms ± 51.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

TODO:

  • Add trait for randomly generating teams.
  • Add method to iterate through record fields/fieldnames and build macro to construct SQL statements.
    • Manually adding fields is tedious and error-prone.
  • Build lexer to parse raw effect text into Effect struct.
  • Add feature flags for each pack.
  • Improve interface.
    • Function arguments with impls like AsRef and Into/TryInto. ex. Shop.add_item
    • Type state pattern with Shop<Open/Close>

Sources

Dependencies

~38MB
~620K SLoC