Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
451 changes: 451 additions & 0 deletions SerialPrograms/Source/Pokemon/Pokemon_BdspRng.cpp

Large diffs are not rendered by default.

171 changes: 171 additions & 0 deletions SerialPrograms/Source/Pokemon/Pokemon_BdspRng.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/* BDSP RNG
*
* From: https://github.com/PokemonAutomation/
*
*/

#ifndef PokemonAutomation_Pokemon_BdspRng_H
#define PokemonAutomation_Pokemon_BdspRng_H

#include <stddef.h>
#include <stdint.h>
#include <functional>
#include <string>
#include <vector>
#include "Pokemon_NatureChecker.h"
#include "Pokemon_StatsCalculation.h"
#include "Pokemon_Xoroshiro128Plus.h"
#include "Pokemon_Xorshift128.h"

namespace PokemonAutomation{
class Cancellable;
namespace Pokemon{


uint64_t bdsp_splitmix64(uint64_t seed);


inline uint64_t bdsp_sign_extend_seed(uint32_t seed){
return (uint64_t)(int64_t)(int32_t)seed;
}

class XoroshiroBDSP{
public:
explicit XoroshiroBDSP(uint64_t seed);

uint64_t next(){ return m_rng.next(); }
uint32_t next_uint(uint32_t maximum){ return (uint32_t)((m_rng.next() >> 32) % maximum); }

private:
Xoroshiro128Plus m_rng;
};


enum class BdspShiny : uint8_t{
None = 0,
Star = 1,
Square = 2,
};
const char* bdsp_shiny_name(BdspShiny shiny);

enum class BdspGender : uint8_t{
Male = 0,
Female = 1,
Genderless = 2,
};
const char* bdsp_gender_name(BdspGender gender);

struct BdspIVs{
uint8_t hp = 0;
uint8_t attack = 0;
uint8_t defense = 0;
uint8_t spatk = 0;
uint8_t spdef = 0;
uint8_t speed = 0;

uint8_t& operator[](size_t index);
uint8_t operator[](size_t index) const;

std::string to_string() const;
};

struct BdspPokemonResult{
uint32_t ec = 0;
uint32_t pid = 0;
BdspShiny shiny = BdspShiny::None;
BdspIVs ivs;
uint8_t ability = 0;
BdspGender gender = BdspGender::Genderless;
uint8_t nature = 0; // Game index, 0-24. See bdsp_nature_name().
uint8_t level = 1;
uint8_t height = 0;
uint8_t weight = 0;

std::string to_string() const;
};

struct BdspIdResult{
uint32_t sidtid = 0;
uint16_t tid = 0;
uint16_t sid = 0;
uint32_t display_tid = 0;

uint16_t tsv() const{ return (uint16_t)(tid ^ sid); }
std::string to_string() const;
};


const char* bdsp_nature_name(uint8_t nature);
NatureCheckerValue bdsp_nature_to_checker_value(uint8_t nature);

// psv == tsv is a square shiny; a difference under 16 is a star
BdspShiny bdsp_get_shiny(uint32_t pid, uint16_t tsv);
bool bdsp_is_shiny(uint32_t pid, uint16_t tsv);


const uint8_t BDSP_NO_SYNCHRONIZE = 0xff;

struct BdspStaticTemplate{
std::string species;
uint8_t level = 1;
uint8_t guaranteed_ivs = 0;
uint8_t ability_kind = 3;
uint8_t gender_ratio = 255;
bool shiny_locked = false;
bool roamer = false;
};

BdspPokemonResult bdsp_generate_static(
Xorshift128 rng, const BdspStaticTemplate& tmpl,
uint16_t tsv, uint8_t synchronize_nature = BDSP_NO_SYNCHRONIZE
);
BdspPokemonResult bdsp_generate_roamer(
Xorshift128 rng, const BdspStaticTemplate& tmpl,
uint16_t tsv, uint8_t synchronize_nature = BDSP_NO_SYNCHRONIZE
);
BdspPokemonResult bdsp_generate(
Xorshift128 rng, const BdspStaticTemplate& tmpl,
uint16_t tsv, uint8_t synchronize_nature = BDSP_NO_SYNCHRONIZE
);

BdspIdResult bdsp_generate_id(Xorshift128 rng);


struct BdspRngHit{
uint64_t advances = 0;
Xorshift128State state;
BdspPokemonResult result;
};


class BdspStaticSearcher{
public:
BdspStaticSearcher(
const Xorshift128State& base_state,
BdspStaticTemplate tmpl,
uint16_t tsv,
uint8_t synchronize_nature = BDSP_NO_SYNCHRONIZE
);

const BdspStaticTemplate& pokemon_template() const{ return m_template; }

BdspPokemonResult generate(uint64_t advances) const;

std::vector<BdspRngHit> scan(
uint64_t min_advances, uint64_t max_advances,
const std::function<bool(const BdspPokemonResult&)>& accept,
bool stop_at_first = false,
Cancellable* cancellable = nullptr
) const;

private:
Xorshift128State m_base_state;
BdspStaticTemplate m_template;
uint16_t m_tsv;
uint8_t m_synchronize_nature;
};


}
}
#endif
187 changes: 187 additions & 0 deletions SerialPrograms/Source/Pokemon/Pokemon_Gf2Matrix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/* GF(2) Linear Algebra
*
* From: https://github.com/PokemonAutomation/
*
*/

#include <bit>
#include "Common/Cpp/Exceptions.h"
#include "Pokemon_Gf2Matrix.h"

namespace PokemonAutomation{
namespace Pokemon{



//
// Gf2Vec128
//

bool Gf2Vec128::get(size_t index) const{
return index < 64
? ((high >> (63 - index)) & 1) != 0
: ((low >> (127 - index)) & 1) != 0;
}
void Gf2Vec128::set(size_t index, bool value){
uint64_t& word = index < 64 ? high : low;
uint64_t mask = (uint64_t)1 << (index < 64 ? 63 - index : 127 - index);
if (value){
word |= mask;
}else{
word &= ~mask;
}
}
bool Gf2Vec128::dot(const Gf2Vec128& x) const{
int bits = std::popcount(high & x.high) + std::popcount(low & x.low);
return (bits & 1) != 0;
}



Gf2Matrix128 Gf2Matrix128::identity(){
Gf2Matrix128 ret;
for (size_t c = 0; c < 128; c++){
ret.m_rows[c].set(c, true);
}
return ret;
}

// row i of the product is the XOR of every row k in x that has x[k][i] == 1.
Gf2Matrix128 Gf2Matrix128::operator*(const Gf2Matrix128& x) const{
Gf2Matrix128 ret;
for (size_t i = 0; i < 128; i++){
Gf2Vec128 accumulator;
uint64_t bits = m_rows[i].high;
while (bits != 0){
// index = 63 - position.
accumulator ^= x.m_rows[63 - (size_t)std::countr_zero(bits)];
bits &= bits - 1;
}
bits = m_rows[i].low;
while (bits != 0){
// index = 127 - position.
accumulator ^= x.m_rows[127 - (size_t)std::countr_zero(bits)];
bits &= bits - 1;
}
ret.m_rows[i] = accumulator;
}
return ret;
}
Gf2Vec128 Gf2Matrix128::operator*(const Gf2Vec128& column) const{
Gf2Vec128 ret;
for (size_t c = 0; c < 128; c++){
ret.set(c, m_rows[c].dot(column));
}
return ret;
}

Gf2Matrix128 Gf2Matrix128::pow(uint64_t exponent) const{
Gf2Matrix128 ret = identity();
Gf2Matrix128 base = *this;
while (exponent != 0){
if ((exponent & 1) != 0){
ret = ret * base;
}
exponent >>= 1;
if (exponent != 0){
base = base * base;
}
}
return ret;
}

Gf2SolveResult gf2_solve_128(
const std::vector<Gf2Vec128>& equations,
const std::vector<bool>& rhs
){
if (equations.size() != rhs.size()){
throw InternalProgramError(
nullptr, PA_CURRENT_FUNCTION,
"gf2_solve_128(): Coefficient and constant counts do not match."
);
}

// Augmented system. We reduce to row echelon form, then back-substitute.
std::vector<Gf2Vec128> rows = equations;
std::vector<bool> constants = rhs;
const size_t height = rows.size();

// pivot_row[c] is the row that owns column "c" as its pivot, or NO_PIVOT.
const size_t NO_PIVOT = (size_t)0 - 1;
std::array<size_t, 128> pivot_row;
pivot_row.fill(NO_PIVOT);

size_t next_row = 0;
for (size_t column = 0; column < 128 && next_row < height; column++){
size_t pivot = NO_PIVOT;
for (size_t row = next_row; row < height; row++){
if (rows[row].get(column)){
pivot = row;
break;
}
}
if (pivot == NO_PIVOT){
continue;
}

std::swap(rows[next_row], rows[pivot]);
{
// std::vector<bool> has no swappable references.
bool tmp = constants[next_row];
constants[next_row] = constants[pivot];
constants[pivot] = tmp;
}

for (size_t row = 0; row < height; row++){
if (row != next_row && rows[row].get(column)){
rows[row] ^= rows[next_row];
constants[row] = constants[row] != constants[next_row];
}
}

pivot_row[column] = next_row;
next_row++;
}

Gf2SolveResult result;

// Any all-zero row with a nonzero constant makes the system unsolvable.
for (size_t row = 0; row < height; row++){
if (rows[row].is_zero() && constants[row]){
return result;
}
}
result.consistent = true;

// Free variables are set to zero, so each pivot variable is just its constant.
for (size_t column = 0; column < 128; column++){
if (pivot_row[column] != NO_PIVOT){
result.solution.set(column, constants[pivot_row[column]]);
}
}

// One null space basis vector per free column.
for (size_t column = 0; column < 128; column++){
if (pivot_row[column] != NO_PIVOT){
continue;
}
Gf2Vec128 basis;
basis.set(column, true);
for (size_t pivot_column = 0; pivot_column < 128; pivot_column++){
size_t row = pivot_row[pivot_column];
if (row != NO_PIVOT && rows[row].get(column)){
basis.set(pivot_column, true);
}
}
result.null_space_basis.emplace_back(basis);
}
result.null_space_dimension = result.null_space_basis.size();

return result;
}




}
}
Loading
Loading