Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
using System ;
using System.Buffers ;
2020-06-27 18:06:28 +00:00
2022-06-18 18:04:24 +00:00
namespace PKHeX.Core ;
2022-08-21 08:39:16 +00:00
/// <summary>
/// Logic for suggesting a moveset.
/// </summary>
2022-06-18 18:04:24 +00:00
public static class MoveListSuggest
2020-06-27 18:06:28 +00:00
{
2023-03-22 01:19:55 +00:00
private static void GetSuggestedMoves ( PKM pk , EvolutionHistory evoChains , MoveSourceType types , IEncounterTemplate enc , Span < ushort > moves )
2020-06-27 18:06:28 +00:00
{
2023-01-22 04:02:33 +00:00
if ( pk is { IsEgg : true , Format : < = 5 } ) // pre relearn
2022-08-22 00:34:32 +00:00
{
2023-04-21 04:23:15 +00:00
var source = GameData . GetLearnSource ( enc . Version ) ;
source . SetEncounterMoves ( enc . Species , 0 , enc . LevelMin , moves ) ;
2023-03-22 01:19:55 +00:00
return ;
2022-08-22 00:34:32 +00:00
}
2020-06-27 18:06:28 +00:00
2023-03-22 01:19:55 +00:00
if ( types is not ( MoveSourceType . None or MoveSourceType . Encounter ) )
{
GetValidMoves ( pk , enc , evoChains , moves , types ) ;
return ;
}
2020-06-27 18:06:28 +00:00
2022-06-18 18:04:24 +00:00
// try to give current moves
2023-07-06 04:14:09 +00:00
if ( enc . Generation < = 2 & & pk . Format < 8 )
2020-06-27 18:06:28 +00:00
{
2022-06-18 18:04:24 +00:00
var lvl = pk . Format > = 7 ? pk . Met_Level : pk . CurrentLevel ;
2023-04-21 04:23:15 +00:00
var source = GameData . GetLearnSource ( enc . Version ) ;
source . SetEncounterMoves ( enc . Species , 0 , lvl , moves ) ;
2023-03-22 01:19:55 +00:00
return ;
2020-06-27 18:06:28 +00:00
}
2020-12-25 00:36:50 +00:00
2023-07-06 04:14:09 +00:00
if ( pk . Species = = enc . Species | | pk . Context . Generation ( ) > = 8 )
2020-12-25 00:36:50 +00:00
{
2023-04-21 04:23:15 +00:00
var game = ( GameVersion ) pk . Version ; // account for SW/SH foreign mutated versions
2023-07-06 04:14:09 +00:00
if ( pk . Context . Generation ( ) > = 8 )
game = pk . Context . GetSingleGameVersion ( ) ;
2023-04-21 04:23:15 +00:00
var source = GameData . GetLearnSource ( game ) ;
source . SetEncounterMoves ( pk . Species , pk . Form , pk . CurrentLevel , moves ) ;
2023-03-22 01:19:55 +00:00
return ;
2020-12-25 00:36:50 +00:00
}
2023-03-22 01:19:55 +00:00
GetValidMoves ( pk , enc , evoChains , moves , types ) ;
2022-06-18 18:04:24 +00:00
}
2023-03-22 01:19:55 +00:00
private static void GetValidMoves ( PKM pk , IEncounterTemplate enc , EvolutionHistory evoChains , Span < ushort > moves , MoveSourceType types = MoveSourceType . ExternalSources )
2022-06-18 18:04:24 +00:00
{
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
var length = pk . MaxMoveID + 1 ;
bool [ ] rent = ArrayPool < bool > . Shared . Rent ( length ) ;
2022-08-18 06:48:37 +00:00
var span = rent . AsSpan ( 0 , length ) ;
LearnPossible . Get ( pk , enc , evoChains , span , types ) ;
2022-06-18 18:04:24 +00:00
2022-08-18 06:48:37 +00:00
var count = span [ 1. . ] . Count ( true ) ;
2023-03-22 01:19:55 +00:00
int remain = moves . Length ;
if ( count < = remain )
LoadAll ( moves , span ) ;
else
LoadSample ( moves , span , count , remain ) ;
span . Clear ( ) ;
ArrayPool < bool > . Shared . Return ( rent ) ;
}
private static void LoadSample ( Span < ushort > moves , ReadOnlySpan < bool > span , int count , int remain )
{
// Selection Sampling
2022-08-18 06:48:37 +00:00
int ctr = 0 ;
2023-03-22 01:19:55 +00:00
var rnd = Util . Rand ;
2022-08-27 06:43:36 +00:00
for ( ushort i = 1 ; i < span . Length ; i + + )
2020-12-25 00:36:50 +00:00
{
2023-03-22 01:19:55 +00:00
if ( ! span [ i ] )
continue ;
if ( rnd . Next ( count - - ) > = remain )
continue ;
moves [ ctr + + ] = i ;
if ( - - remain = = 0 )
break ;
2020-12-25 00:36:50 +00:00
}
2023-03-22 01:19:55 +00:00
}
2020-12-25 00:36:50 +00:00
2023-03-22 01:19:55 +00:00
private static void LoadAll ( Span < ushort > moves , ReadOnlySpan < bool > span )
{
int ctr = 0 ;
for ( ushort i = 1 ; i < span . Length ; i + + )
{
if ( ! span [ i ] )
continue ;
moves [ ctr + + ] = i ;
}
2022-06-18 18:04:24 +00:00
}
2021-03-31 01:51:53 +00:00
2022-06-18 18:04:24 +00:00
/// <summary>
/// Gets four moves which can be learned depending on the input arguments.
/// </summary>
/// <param name="analysis">Parse information to generate a moveset for.</param>
2023-03-22 01:19:55 +00:00
/// <param name="moves">Result storage</param>
2022-06-18 18:04:24 +00:00
/// <param name="types">Allowed move sources for populating the result array</param>
2023-03-22 01:19:55 +00:00
public static void GetSuggestedCurrentMoves ( this LegalityAnalysis analysis , Span < ushort > moves , MoveSourceType types = MoveSourceType . All )
2022-06-18 18:04:24 +00:00
{
if ( ! analysis . Parsed )
2023-03-22 01:19:55 +00:00
return ;
2022-06-18 18:04:24 +00:00
var pk = analysis . Entity ;
2023-01-22 04:02:33 +00:00
if ( pk is { IsEgg : true , Format : > = 6 } )
2023-03-22 01:19:55 +00:00
{
pk . GetRelearnMoves ( moves ) ;
return ;
}
2021-03-31 01:51:53 +00:00
2022-06-18 18:04:24 +00:00
if ( pk . IsEgg )
types = types . ClearNonEggSources ( ) ;
2021-04-08 22:58:09 +00:00
2022-06-18 18:04:24 +00:00
var info = analysis . Info ;
2023-03-22 01:19:55 +00:00
GetSuggestedMoves ( pk , info . EvoChainsAllGens , types , info . EncounterOriginal , moves ) ;
2022-06-18 18:04:24 +00:00
}
2021-04-05 01:30:01 +00:00
2022-06-18 18:04:24 +00:00
/// <summary>
/// Gets the current <see cref="PKM.RelearnMoves"/> array of four moves that might be legal.
/// </summary>
/// <remarks>Use <see cref="GetSuggestedRelearnMovesFromEncounter"/> instead of calling directly; this method just puts default values in without considering the final moveset.</remarks>
2023-03-22 01:19:55 +00:00
public static void GetSuggestedRelearn ( this IEncounterTemplate enc , PKM pk , Span < ushort > moves )
2022-06-18 18:04:24 +00:00
{
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
if ( LearnVerifierRelearn . ShouldNotHaveRelearnMoves ( enc , pk ) )
2023-03-22 01:19:55 +00:00
return ;
2021-04-08 22:58:09 +00:00
2023-03-22 01:19:55 +00:00
GetSuggestedRelearnInternal ( enc , pk , moves ) ;
2022-06-18 18:04:24 +00:00
}
2021-04-08 22:58:09 +00:00
2022-06-18 18:04:24 +00:00
// Invalid encounters won't be recognized as an EncounterEgg; check if it *should* be a bred egg.
2023-03-22 01:19:55 +00:00
private static void GetSuggestedRelearnInternal ( this IEncounterTemplate enc , PKM pk , Span < ushort > moves )
2022-06-18 18:04:24 +00:00
{
2023-03-22 01:19:55 +00:00
if ( enc is IRelearn { Relearn : { HasMoves : true } r } )
r . CopyTo ( moves ) ;
else if ( enc is EncounterEgg or EncounterInvalid { EggEncounter : true } )
GetSuggestedRelearnEgg ( enc , pk , moves ) ;
}
2021-04-08 22:58:09 +00:00
2023-03-22 01:19:55 +00:00
private static void GetSuggestedRelearnEgg ( IEncounterTemplate enc , PKM pk , Span < ushort > moves )
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
{
2022-08-27 06:43:36 +00:00
Span < ushort > current = stackalloc ushort [ 4 ] ;
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
pk . GetRelearnMoves ( current ) ;
2022-08-27 06:43:36 +00:00
Span < ushort > expected = stackalloc ushort [ current . Length ] ;
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
_ = MoveBreed . GetExpectedMoves ( current , enc , expected ) ;
2023-03-22 01:19:55 +00:00
expected . CopyTo ( moves ) ;
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
}
2022-06-18 18:04:24 +00:00
/// <summary>
/// Gets the current <see cref="PKM.RelearnMoves"/> array of four moves that might be legal.
/// </summary>
2023-03-22 01:19:55 +00:00
public static void GetSuggestedRelearnMovesFromEncounter ( this LegalityAnalysis analysis , Span < ushort > moves , IEncounterTemplate ? enc = null )
2022-06-18 18:04:24 +00:00
{
var info = analysis . Info ;
enc ? ? = info . EncounterOriginal ;
var pk = analysis . Entity ;
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
if ( LearnVerifierRelearn . ShouldNotHaveRelearnMoves ( enc , pk ) )
2023-03-22 01:19:55 +00:00
return ;
2022-06-18 18:04:24 +00:00
if ( enc is EncounterEgg or EncounterInvalid { EggEncounter : true } )
2023-04-02 23:19:56 +00:00
enc . GetSuggestedRelearnEgg ( info . Moves , pk , moves ) ;
2023-03-22 01:19:55 +00:00
else
enc . GetSuggestedRelearnInternal ( pk , moves ) ;
2022-06-18 18:04:24 +00:00
}
2023-03-22 01:19:55 +00:00
private static void GetSuggestedRelearnEgg ( this IEncounterTemplate enc , ReadOnlySpan < MoveResult > parse , PKM pk , Span < ushort > moves )
2022-06-18 18:04:24 +00:00
{
2023-03-22 01:19:55 +00:00
enc . GetEggRelearnMoves ( parse , pk , moves ) ;
2022-06-18 18:04:24 +00:00
int generation = enc . Generation ;
if ( generation < = 5 ) // gen2 does not have splitbreed, <=5 do not have relearn moves and shouldn't even be here.
2023-03-22 01:19:55 +00:00
return ;
2022-06-18 18:04:24 +00:00
// Split-breed species like Budew & Roselia may be legal for one, and not the other.
// If we're not a split-breed or are already legal, return.
2023-07-08 19:46:46 +00:00
if ( ! Breeding . IsSplitBreedNotBabySpecies ( enc . Species , generation ) )
2023-03-22 01:19:55 +00:00
return ;
2022-06-18 18:04:24 +00:00
var tmp = pk . Clone ( ) ;
2023-03-22 01:19:55 +00:00
tmp . SetRelearnMoves ( moves ) ;
2022-06-18 18:04:24 +00:00
var la = new LegalityAnalysis ( tmp ) ;
2023-03-22 01:19:55 +00:00
var chk = la . Info . Moves ;
if ( MoveResult . AllValid ( chk ) )
return ;
2022-06-18 18:04:24 +00:00
// Try again with the other split-breed species if possible.
2023-01-22 04:02:33 +00:00
var generator = EncounterGenerator . GetGenerator ( enc . Version ) ;
2023-07-06 04:14:09 +00:00
Span < EvoCriteria > chain = stackalloc EvoCriteria [ EvolutionTree . MaxEvolutions ] ;
2023-09-05 01:23:10 +00:00
var origin = new EvolutionOrigin ( enc . Species , ( byte ) enc . Version , ( byte ) enc . Generation , 1 , 100 , OriginOptions . EncounterTemplate ) ;
2023-07-06 04:14:09 +00:00
int count = EvolutionChain . GetOriginChain ( chain , pk , origin ) ;
for ( int i = 0 ; i < count ; i + + )
{
if ( chain [ i ] . Species ! = enc . Species )
continue ;
count = i ;
break ;
}
var evos = chain [ . . count ] . ToArray ( ) ;
var other = generator . GetPossible ( pk , evos , enc . Version , EncounterTypeGroup . Egg ) ;
2022-08-18 06:48:37 +00:00
foreach ( var incense in other )
{
2023-03-22 01:19:55 +00:00
if ( incense . Species = = enc . Species )
continue ;
incense . GetEggRelearnMoves ( parse , pk , moves ) ;
break ;
2022-08-18 06:48:37 +00:00
}
2022-06-18 18:04:24 +00:00
}
2023-03-26 00:48:49 +00:00
private static void GetEggRelearnMoves ( this IEncounterTemplate enc , ReadOnlySpan < MoveResult > parse , PKM pk , Span < ushort > moves )
2022-06-18 18:04:24 +00:00
{
// Extract a list of the moves that should end up in the relearn move list.
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
LoadRelearnFlagged ( moves , parse , pk ) ;
2021-04-10 18:03:21 +00:00
2022-08-27 06:43:36 +00:00
Span < ushort > expected = stackalloc ushort [ moves . Length ] ;
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
_ = MoveBreed . GetExpectedMoves ( moves , enc , expected ) ;
2023-03-26 00:48:49 +00:00
expected . CopyTo ( moves ) ;
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
}
2022-08-27 06:43:36 +00:00
private static void LoadRelearnFlagged ( Span < ushort > moves , ReadOnlySpan < MoveResult > parse , PKM pk )
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
{
// Loads only indexes that are flagged as relearn moves
int count = 0 ;
for ( int index = 0 ; index < parse . Length ; index + + )
2021-04-08 22:58:09 +00:00
{
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
var move = parse [ index ] ;
if ( move . ShouldBeInRelearnMoves ( ) )
moves [ count + + ] = pk . GetMove ( index ) ;
2021-04-08 22:58:09 +00:00
}
2023-04-02 23:19:56 +00:00
moves [ count . . ] . Clear ( ) ;
2020-06-27 18:06:28 +00:00
}
}