From 39e078d595d64694580134f58040723e7da8b33d Mon Sep 17 00:00:00 2001 From: Kurt Date: Fri, 23 Nov 2018 16:00:27 -0800 Subject: [PATCH] Add learn randomizer untested, committing what I have for now - metronome mode - expand only (set all new to pound) --- .../Randomizers/LearnsetRandomizer.cs | 157 ++++++++++++++++++ .../Randomizers/MoveRandomizer.cs | 154 +++++++++++++++++ .../Randomizers/Settings/LearnSettings.cs | 34 ++++ .../{ => Settings}/RandSettings.cs | 0 .../{ => Settings}/SpeciesSettings.cs | 0 pkNX.Randomization/SpeciesSetting.cs | 38 ----- pkNX.Structures/Learnset/Learnset.cs | 8 + pkNX.WinForms/Subforms/PokeDataUI.Designer.cs | 70 +++++--- pkNX.WinForms/Subforms/PokeDataUI.cs | 36 +++- 9 files changed, 435 insertions(+), 62 deletions(-) create mode 100644 pkNX.Randomization/Randomizers/LearnsetRandomizer.cs create mode 100644 pkNX.Randomization/Randomizers/MoveRandomizer.cs create mode 100644 pkNX.Randomization/Randomizers/Settings/LearnSettings.cs rename pkNX.Randomization/Randomizers/{ => Settings}/RandSettings.cs (100%) rename pkNX.Randomization/Randomizers/{ => Settings}/SpeciesSettings.cs (100%) delete mode 100644 pkNX.Randomization/SpeciesSetting.cs diff --git a/pkNX.Randomization/Randomizers/LearnsetRandomizer.cs b/pkNX.Randomization/Randomizers/LearnsetRandomizer.cs new file mode 100644 index 00000000..0b998485 --- /dev/null +++ b/pkNX.Randomization/Randomizers/LearnsetRandomizer.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using pkNX.Structures; + +namespace pkNX.Randomization +{ + /// + /// randomizer. + /// + 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 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; + } + } +} \ No newline at end of file diff --git a/pkNX.Randomization/Randomizers/MoveRandomizer.cs b/pkNX.Randomization/Randomizers/MoveRandomizer.cs new file mode 100644 index 00000000..3910c6d4 --- /dev/null +++ b/pkNX.Randomization/Randomizers/MoveRandomizer.cs @@ -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 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 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; + } + } +} \ No newline at end of file diff --git a/pkNX.Randomization/Randomizers/Settings/LearnSettings.cs b/pkNX.Randomization/Randomizers/Settings/LearnSettings.cs new file mode 100644 index 00000000..00d263b1 --- /dev/null +++ b/pkNX.Randomization/Randomizers/Settings/LearnSettings.cs @@ -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; + } +} \ No newline at end of file diff --git a/pkNX.Randomization/Randomizers/RandSettings.cs b/pkNX.Randomization/Randomizers/Settings/RandSettings.cs similarity index 100% rename from pkNX.Randomization/Randomizers/RandSettings.cs rename to pkNX.Randomization/Randomizers/Settings/RandSettings.cs diff --git a/pkNX.Randomization/Randomizers/SpeciesSettings.cs b/pkNX.Randomization/Randomizers/Settings/SpeciesSettings.cs similarity index 100% rename from pkNX.Randomization/Randomizers/SpeciesSettings.cs rename to pkNX.Randomization/Randomizers/Settings/SpeciesSettings.cs diff --git a/pkNX.Randomization/SpeciesSetting.cs b/pkNX.Randomization/SpeciesSetting.cs deleted file mode 100644 index e928ef59..00000000 --- a/pkNX.Randomization/SpeciesSetting.cs +++ /dev/null @@ -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; - } - } -} diff --git a/pkNX.Structures/Learnset/Learnset.cs b/pkNX.Structures/Learnset/Learnset.cs index 188953e8..efd95114 100644 --- a/pkNX.Structures/Learnset/Learnset.cs +++ b/pkNX.Structures/Learnset/Learnset.cs @@ -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; + } } } diff --git a/pkNX.WinForms/Subforms/PokeDataUI.Designer.cs b/pkNX.WinForms/Subforms/PokeDataUI.Designer.cs index 036d75e7..35612892 100644 --- a/pkNX.WinForms/Subforms/PokeDataUI.Designer.cs +++ b/pkNX.WinForms/Subforms/PokeDataUI.Designer.cs @@ -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; } } \ No newline at end of file diff --git a/pkNX.WinForms/Subforms/PokeDataUI.cs b/pkNX.WinForms/Subforms/PokeDataUI.cs index 9bb2ee4f..700b1adf 100644 --- a/pkNX.WinForms/Subforms/PokeDataUI.cs +++ b/pkNX.WinForms/Subforms/PokeDataUI.cs @@ -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)