Add learn randomizer

untested, committing what I have for now
- metronome mode
- expand only (set all new to pound)
This commit is contained in:
Kurt
2018-11-23 16:00:27 -08:00
parent d26bcfc12e
commit 39e078d595
9 changed files with 435 additions and 62 deletions

View File

@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Linq;
using pkNX.Structures;
namespace pkNX.Randomization
{
/// <summary>
/// <see cref="Learnset"/> randomizer.
/// </summary>
public class LearnsetRandomizer : Randomizer
{
private readonly Learnset[] Learnsets;
private readonly GameInfo Game;
public readonly MoveRandomizer moverand;
public PersonalTable Personal;
public Move[] Moves;
public LearnSettings Settings { get; set; }
public LearnsetRandomizer(GameInfo game, Learnset[] learnsets, PersonalTable t)
{
Game = game;
Learnsets = learnsets;
Personal = t;
}
private static readonly int[] MetronomeMove = { 118 };
private static readonly int[] MetronomeLevel = { 1 };
public void ExecuteMetronome()
{
foreach (var learn in Learnsets)
learn.Update(MetronomeMove, MetronomeLevel);
}
public void ExecuteExpandOnly()
{
foreach (var learn in Learnsets)
{
var count = learn.Count;
if (count == 0)
continue;
if (count >= Settings.ExpandTo)
continue;
int diff = Settings.ExpandTo - count;
var moves = learn.Moves;
Array.Resize(ref moves, Settings.ExpandTo);
var levels = learn.Moves;
Array.Resize(ref levels, Settings.ExpandTo);
for (int i = count; i < Settings.ExpandTo; i++)
{
moves[i] = 1;
levels[i] = Math.Min(100, levels[count - 1] + diff);
}
learn.Update(moves, levels);
}
}
public void Initialize(Move[] moves, LearnSettings settings)
{
Moves = moves;
Settings = settings;
moverand.STAB = settings.STAB;
moverand.STABPercent = settings.STABPercent;
}
public LearnsetRandomizer(Learnset[] sets)
{
moverand = new MoveRandomizer(Game, Moves, Personal);
Learnsets = sets;
Settings.STABPercent = 52.3m;
}
public IList<int> BannedMoves { set => moverand.BannedMoves = value; }
public override void Execute()
{
for (var i = 0; i < Learnsets.Length; i++)
Randomize(Learnsets[i], i);
}
private void Randomize(Learnset set, int index)
{
int[] moves = GetRandomMoves(set.Count, index);
int[] levels = GetRandomLevels(set, moves.Length);
if (Settings.Learn4Level1)
{
for (int i = 0; i < Math.Min(4, levels.Length); ++i)
levels[i] = 1;
}
set.Update(moves, levels);
}
private int[] GetRandomLevels(Learnset set, int count)
{
int[] levels = new int[count];
if (count == 0)
return levels;
if (Settings.Spread)
{
levels[0] = 1;
decimal increment = Settings.SpreadTo / (decimal)count;
for (int i = 1; i < count; i++)
levels[i] = (int)(i * increment);
return levels;
}
if (levels.Length == count && levels.Length == set.Levels.Length)
return set.Levels; // don't modify
var exist = set.Levels;
int lastlevel = Math.Min(1, exist.LastOrDefault());
exist.CopyTo(levels, 0);
for (int i = exist.Length; i < levels.Length; i++)
levels[i] = Math.Max(100, lastlevel + (exist.Length - i + 1));
return levels;
}
private int[] GetRandomMoves(int count, int index)
{
count = Settings.Expand ? Settings.ExpandTo : count;
int[] moves = new int[count];
if (count == 0)
return moves;
moves[0] = Settings.STABFirst ? moverand.GetRandomFirstMove(index) : moverand.GetRandomFirstMoveAny();
var rand = moverand.GetRandomLearnset(index, count - 1);
// STAB Moves (if requested) come first; randomize the order of moves
Util.Shuffle(rand);
if (Settings.OrderByPower)
moverand.ReorderMovesPower(rand);
rand.CopyTo(moves, 1);
return moves;
}
public int[] GetHighPoweredMoves(int species, int form, int count = 4)
{
int index = Personal.GetFormeIndex(species, form);
var learn = Learnsets[index];
return learn.GetHighPoweredMoves(count, Moves);
}
public int[] GetCurrentMoves(int species, int form, int level, int count = 4)
{
int i = Personal.GetFormeIndex(species, form);
var moves = Learnsets[i].GetEncounterMoves(level);
Array.Resize(ref moves, count);
return moves;
}
}
}

View File

@@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.Linq;
using pkNX.Structures;
namespace pkNX.Randomization
{
public class MoveRandomizer : Randomizer
{
private readonly Move[] MoveData;
private readonly PersonalTable SpeciesStat;
private readonly GenericRandomizer RandMove;
public MoveRandomizer(GameInfo config, Move[] moves, PersonalTable t)
{
var maxMoveId = config.MaxMoveID;
MoveData = moves;
SpeciesStat = t;
RandMove = new GenericRandomizer(Enumerable.Range(1, maxMoveId - 1).ToArray());
}
public override void Execute() => throw new Exception("Shouldn't be called.");
public bool DMG = true;
public int DMGCount = 2;
public bool STAB = true;
public int STABCount = 2;
public decimal STABPercent = 100;
public IList<int> BannedMoves = new int[0];
public static readonly int[] FixedDamageMoves = { 49, 82 };
public int[] GetRandomLearnset(int index, int movecount) => GetRandomLearnset(SpeciesStat[index].Types, movecount);
public int[] GetRandomLearnset(int[] Types, int movecount)
{
var oldSTABCount = STABCount;
STABCount = (int)(STABPercent * movecount / 100);
int[] moves = GetRandomMoveset(Types, movecount);
STABCount = oldSTABCount;
return moves;
}
public int[] GetRandomMoveset(int index, int movecount = 4) => GetRandomMoveset(SpeciesStat[index].Types, movecount);
public int[] GetRandomMoveset(int[] Types, int movecount = 4)
{
int loopctr = 0;
const int maxLoop = 666;
int[] moves;
do { moves = GetRandomMoves(Types, movecount); }
while (!IsMovesetMeetingRequirements(moves, movecount) && loopctr++ <= maxLoop);
return moves;
}
private int[] GetRandomMoves(int[] Types, int movecount = 4)
{
int[] moves = new int[movecount];
if (STAB)
{
for (int i = 0; i < STABCount; i++)
moves[i] = GetRandomSTABMove(Types);
}
for (int i = 0; i < moves.Length; i++) // remainder of moves
moves[i] = RandMove.Next();
return moves;
}
private int GetRandomSTABMove(int[] types)
{
int move;
do { move = RandMove.Next(); }
while (!types.Contains(MoveData[move].Type));
return move;
}
private bool IsMovesetMeetingRequirements(int[] moves, int count)
{
if (DMG && DMGCount > moves.Count(move => MoveData[move].Category != 0))
return false;
if (moves.Any(BannedMoves.Contains))
return false;
return moves.Distinct().Count() == count;
}
public void ReorderMovesPower(IList<int> moves)
{
var data = moves.Select((Move, Index) => new { Index, Move, Data = MoveData[Move] });
var powered = data.Where(z => z.Data.Power > 1).ToList();
var indexes = powered.Select(z => z.Index).ToList();
var order = powered.OrderBy(z => z.Data.Power * Math.Max(1, (z.Data.HitMin + z.Data.HitMax) / 2m)).ToList();
for (var i = 0; i < order.Count; i++)
moves[indexes[i]] = order[i].Move;
}
private static readonly int[] firstMoves =
{
1, // Pound
40, // Poison Sting
52, // Ember
55, // Water Gun
64, // Peck
71, // Absorb
84, // Thunder Shock
98, // Quick Attack
122, // Lick
141, // Leech Life
};
private static readonly GenericRandomizer first = new GenericRandomizer(firstMoves);
public int GetRandomFirstMoveAny()
{
first.Reset();
return first.Next();
}
public int GetRandomFirstMove(int index) => GetRandomFirstMove(SpeciesStat[index].Types);
public int GetRandomFirstMove(int[] types)
{
first.Reset();
int ctr = 0;
int move;
do
{
move = first.Next();
if (++ctr == firstMoves.Length)
return move;
} while (!types.Contains(MoveData[move].Type));
return move;
}
public bool SanitizeMovesetForBannedMoves(int[] moves, int index)
{
bool updated = false;
for (int m = 0; m < moves.Length; m++)
{
if (!BannedMoves.Contains(moves[m]))
continue;
updated = true;
moves[m] = GetRandomFirstMove(index);
}
return updated;
}
}
}

View File

@@ -0,0 +1,34 @@
using System.ComponentModel;
namespace pkNX.Randomization
{
public class LearnSettings
{
private const string General = nameof(General);
private const string Misc = nameof(Misc);
[Category(General), Description("Expands the learnset to the specified count.")]
public bool Expand { get; set; } = true;
[Category(General), Description("Count to expand the learnset to.")]
public int ExpandTo { get; set; } = 25;
[Category(General), Description("Evenly spreads learned moves out from level 1 to the specified end level.")]
public bool Spread { get; set; } = true;
[Category(General), Description("Level to end learning level up moves.")]
public int SpreadTo { get; set; } = 75;
[Category(General), Description("Requires a certain percent of moves to have STAB.")]
public bool STAB { get; set; }
[Category(General), Description("Required percent of moves having STAB.")]
public decimal STABPercent { get; set; }
[Category(Misc), Description("Reorders moves so that moves are learned with increasing power.")]
public bool OrderByPower { get; set; } = true;
[Category(Misc), Description("Requires the first move learned to be STAB.")]
public bool STABFirst { get; set; } = true;
[Category(Misc), Description("Requires 4 moves to be available at level 1.")]
public bool Learn4Level1 { get; set; } = false;
}
}

View File

@@ -1,38 +0,0 @@
using pkNX.Structures;
namespace pkNX.Randomization
{
public class SpeciesSetting
{
public bool Gen1 { get; set; }
public bool Gen2 { get; set; }
public bool Gen3 { get; set; }
public bool Gen4 { get; set; }
public bool Gen5 { get; set; }
public bool Gen6 { get; set; }
public bool Gen7 { get; set; }
public bool L { get; set; } = false;
public bool E { get; set; } = false;
public bool Shedinja = false;
public bool EXP { get; set; } = false;
public bool BST { get; set; } = true;
public bool Type { get; set; } = false;
private readonly GameVersion Game;
private readonly int Generation;
public SpeciesSetting(GameVersion game)
{
Game = game;
Generation = Game.GetGeneration();
Gen1 = Generation <= 1;
Gen2 = Generation <= 2;
Gen3 = Generation <= 3;
Gen4 = Generation <= 4;
Gen5 = Generation <= 5;
Gen6 = Generation <= 6;
Gen7 = Generation <= 7;
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace pkNX.Structures
{
@@ -85,5 +86,12 @@ public void Update(int[] moves, int[] levels)
Levels = levels;
Count = Moves.Length;
}
public int[] GetHighPoweredMoves(int count, Move[] movedata)
{
var moves = Moves.OrderByDescending(move => movedata[move].Power).Distinct().Take(count).ToArray();
Array.Resize(ref moves, count);
return moves;
}
}
}

View File

@@ -126,14 +126,16 @@ private void InitializeComponent()
this.B_RandPersonal = new System.Windows.Forms.Button();
this.PG_Personal = new System.Windows.Forms.PropertyGrid();
this.Tab_RLearn = new System.Windows.Forms.TabPage();
this.B_LearnMetronome = new System.Windows.Forms.Button();
this.B_LearnExpand = new System.Windows.Forms.Button();
this.B_RandLearn = new System.Windows.Forms.Button();
this.PG_Learn = new System.Windows.Forms.PropertyGrid();
this.Tab_REvo = new System.Windows.Forms.TabPage();
this.B_TradeEvo = new System.Windows.Forms.Button();
this.B_RandEvo = new System.Windows.Forms.Button();
this.PG_Evolution = new System.Windows.Forms.PropertyGrid();
this.CB_Species = new System.Windows.Forms.ComboBox();
this.PB_MonSprite = new System.Windows.Forms.PictureBox();
this.B_RandLearn = new System.Windows.Forms.Button();
this.PG_Learn = new System.Windows.Forms.PropertyGrid();
this.tabControl1.SuspendLayout();
this.Tab_Personal.SuspendLayout();
this.Tab_Learn.SuspendLayout();
@@ -1175,6 +1177,8 @@ private void InitializeComponent()
//
// Tab_RLearn
//
this.Tab_RLearn.Controls.Add(this.B_LearnMetronome);
this.Tab_RLearn.Controls.Add(this.B_LearnExpand);
this.Tab_RLearn.Controls.Add(this.B_RandLearn);
this.Tab_RLearn.Controls.Add(this.PG_Learn);
this.Tab_RLearn.Location = new System.Drawing.Point(4, 22);
@@ -1184,6 +1188,46 @@ private void InitializeComponent()
this.Tab_RLearn.Text = "Randomize Learn";
this.Tab_RLearn.UseVisualStyleBackColor = true;
//
// B_LearnMetronome
//
this.B_LearnMetronome.Location = new System.Drawing.Point(228, 80);
this.B_LearnMetronome.Name = "B_LearnMetronome";
this.B_LearnMetronome.Size = new System.Drawing.Size(135, 23);
this.B_LearnMetronome.TabIndex = 5;
this.B_LearnMetronome.Text = "Metronome Mode";
this.B_LearnMetronome.UseVisualStyleBackColor = true;
this.B_LearnMetronome.Click += new System.EventHandler(this.B_LearnMetronome_Click);
//
// B_LearnExpand
//
this.B_LearnExpand.Location = new System.Drawing.Point(87, 80);
this.B_LearnExpand.Name = "B_LearnExpand";
this.B_LearnExpand.Size = new System.Drawing.Size(135, 23);
this.B_LearnExpand.TabIndex = 4;
this.B_LearnExpand.Text = "Expand Movepools";
this.B_LearnExpand.UseVisualStyleBackColor = true;
this.B_LearnExpand.Click += new System.EventHandler(this.B_LearnExpand_Click);
//
// B_RandLearn
//
this.B_RandLearn.Location = new System.Drawing.Point(6, 80);
this.B_RandLearn.Name = "B_RandLearn";
this.B_RandLearn.Size = new System.Drawing.Size(75, 23);
this.B_RandLearn.TabIndex = 3;
this.B_RandLearn.Text = "Randomize";
this.B_RandLearn.UseVisualStyleBackColor = true;
this.B_RandLearn.Click += new System.EventHandler(this.B_RandLearn_Click);
//
// PG_Learn
//
this.PG_Learn.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.PG_Learn.Location = new System.Drawing.Point(6, 109);
this.PG_Learn.Name = "PG_Learn";
this.PG_Learn.Size = new System.Drawing.Size(411, 350);
this.PG_Learn.TabIndex = 2;
//
// Tab_REvo
//
this.Tab_REvo.Controls.Add(this.B_TradeEvo);
@@ -1245,26 +1289,6 @@ private void InitializeComponent()
this.PB_MonSprite.TabIndex = 91;
this.PB_MonSprite.TabStop = false;
//
// B_RandLearn
//
this.B_RandLearn.Location = new System.Drawing.Point(6, 80);
this.B_RandLearn.Name = "B_RandLearn";
this.B_RandLearn.Size = new System.Drawing.Size(75, 23);
this.B_RandLearn.TabIndex = 3;
this.B_RandLearn.Text = "Randomize";
this.B_RandLearn.UseVisualStyleBackColor = true;
this.B_RandLearn.Click += new System.EventHandler(this.B_RandLearn_Click);
//
// PG_Learn
//
this.PG_Learn.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.PG_Learn.Location = new System.Drawing.Point(6, 109);
this.PG_Learn.Name = "PG_Learn";
this.PG_Learn.Size = new System.Drawing.Size(411, 350);
this.PG_Learn.TabIndex = 2;
//
// PokeDataUI
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -1403,5 +1427,7 @@ private void InitializeComponent()
private System.Windows.Forms.Button B_TradeEvo;
private System.Windows.Forms.Button B_RandLearn;
private System.Windows.Forms.PropertyGrid PG_Learn;
private System.Windows.Forms.Button B_LearnExpand;
private System.Windows.Forms.Button B_LearnMetronome;
}
}

View File

@@ -52,7 +52,7 @@ public PokeDataUI(PokeEditor editor, GameManager rom)
PG_Personal.SelectedObject = new PersonalRandSettings();
PG_Evolution.SelectedObject = new SpeciesSettings();
PG_Learn.SelectedObject = null;
PG_Learn.SelectedObject = new LearnSettings();
}
public GameManager ROM { get; set; }
@@ -469,7 +469,39 @@ private void B_TradeEvo_Click(object sender, EventArgs e)
private void B_RandLearn_Click(object sender, EventArgs e)
{
WinFormsUtil.Alert("Not implemented yet.");
SaveCurrent();
var settings = (LearnSettings)PG_Learn.SelectedObject;
var rand = new LearnsetRandomizer(ROM.Info, Editor.Learn.LoadAll(), Editor.Personal);
rand.Initialize(ROM.Data.MoveData.LoadAll(), settings);
rand.Execute();
LoadIndex(CB_Species.SelectedIndex);
System.Media.SystemSounds.Asterisk.Play();
}
private void B_LearnExpand_Click(object sender, EventArgs e)
{
var settings = (LearnSettings)PG_Learn.SelectedObject;
if (!settings.Expand)
{
WinFormsUtil.Error("Expand moves not selected. Please double check settings.",
"Not expanding learnsets.");
return;
}
var rand = new LearnsetRandomizer(ROM.Info, Editor.Learn.LoadAll(), Editor.Personal);
rand.Initialize(ROM.Data.MoveData.LoadAll(), settings);
rand.ExecuteExpandOnly();
LoadIndex(CB_Species.SelectedIndex);
System.Media.SystemSounds.Asterisk.Play();
}
private void B_LearnMetronome_Click(object sender, EventArgs e)
{
var settings = (LearnSettings)PG_Learn.SelectedObject;
var rand = new LearnsetRandomizer(ROM.Info, Editor.Learn.LoadAll(), Editor.Personal);
rand.Initialize(ROM.Data.MoveData.LoadAll(), settings);
rand.ExecuteMetronome();
LoadIndex(CB_Species.SelectedIndex);
System.Media.SystemSounds.Asterisk.Play();
}
private void B_Save_Click(object sender, EventArgs e)