From 539bc0fb5027178c4d09df1ca0ce5c1d5c4dade8 Mon Sep 17 00:00:00 2001 From: Maruno17 Date: Sun, 18 Jun 2023 23:36:06 +0100 Subject: [PATCH] More AI bug fixes, more work on testing AI --- .../011_Battle/005_AI/005_AI_ChooseMove.rb | 11 + .../011_Battle/005_AI/008_AI_Utilities.rb | 4 +- Data/Scripts/011_Battle/005_AI/012_AIMove.rb | 2 +- .../007_AI_MoveEffects_Items.rb | 2 +- Data/Scripts/022_Maruno/debug battle tests.rb | 333 +++++++++++++++--- 5 files changed, 308 insertions(+), 44 deletions(-) diff --git a/Data/Scripts/011_Battle/005_AI/005_AI_ChooseMove.rb b/Data/Scripts/011_Battle/005_AI/005_AI_ChooseMove.rb index 831c7332b..1277ba658 100644 --- a/Data/Scripts/011_Battle/005_AI/005_AI_ChooseMove.rb +++ b/Data/Scripts/011_Battle/005_AI/005_AI_ChooseMove.rb @@ -22,6 +22,17 @@ class Battle::AI # against it. def pbGetMoveScores choices = [] + + # TODO: Delete this after testing AI. + if $tested_abilities && @user.ability_id + $tested_abilities[@user.ability_id] ||= 0 + $tested_abilities[@user.ability_id] += 1 + end + if $tested_items && @user.item_id + $tested_items[@user.item_id] ||= 0 + $tested_items[@user.item_id] += 1 + end + @user.battler.eachMoveWithIndex do |orig_move, idxMove| # TODO: Delete this after testing AI. diff --git a/Data/Scripts/011_Battle/005_AI/008_AI_Utilities.rb b/Data/Scripts/011_Battle/005_AI/008_AI_Utilities.rb index 4c9195986..b4ae0d3c8 100644 --- a/Data/Scripts/011_Battle/005_AI/008_AI_Utilities.rb +++ b/Data/Scripts/011_Battle/005_AI/008_AI_Utilities.rb @@ -456,7 +456,7 @@ Battle::AI::Handlers::ItemRanking.add(:BLACKSLUDGE, Battle::AI::Handlers::ItemRanking.add(:CHESTOBERRY, proc { |item, score, battler, ai| if ai.trainer.high_skill? - score += 1 if battler.has_move_with_function("HealUserFullyAndFallAsleep") + score += 1 if battler.has_move_with_function?("HealUserFullyAndFallAsleep") end next score } @@ -784,7 +784,7 @@ Battle::AI::Handlers::ItemRanking.add(:TOXICORB, Battle::AI::Handlers::ItemRanking.add(:WHITEHERB, proc { |item, score, battler, ai| if ai.trainer.high_skill? - score += 1 if battler.has_move_with_function("LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2") + score += 1 if battler.has_move_with_function?("LowerUserDefSpDef1RaiseUserAtkSpAtkSpd2") end next score } diff --git a/Data/Scripts/011_Battle/005_AI/012_AIMove.rb b/Data/Scripts/011_Battle/005_AI/012_AIMove.rb index fa45cae7f..cdcc7a761 100644 --- a/Data/Scripts/011_Battle/005_AI/012_AIMove.rb +++ b/Data/Scripts/011_Battle/005_AI/012_AIMove.rb @@ -453,7 +453,7 @@ class Battle::AI::AIMove if user.item_active? if user.item == :ZOOMLENS if rough_priority(user) <= 0 - mods[:accuracy_multiplier] *= 1.2 if target.faster_than?(user) + modifiers[:accuracy_multiplier] *= 1.2 if target.faster_than?(user) end else Battle::ItemEffects.triggerAccuracyCalcFromUser( diff --git a/Data/Scripts/011_Battle/006_AI MoveEffects/007_AI_MoveEffects_Items.rb b/Data/Scripts/011_Battle/006_AI MoveEffects/007_AI_MoveEffects_Items.rb index f521396ea..f766fc574 100644 --- a/Data/Scripts/011_Battle/006_AI MoveEffects/007_AI_MoveEffects_Items.rb +++ b/Data/Scripts/011_Battle/006_AI MoveEffects/007_AI_MoveEffects_Items.rb @@ -147,7 +147,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DestroyTargetBerryOrGem" #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CorrodeTargetItem", proc { |move, user, target, ai, battle| - next true if !target.item || target.unlosableItem?(target.item) || + next true if !target.item || target.battler.unlosableItem?(target.item) || target.effects[PBEffects::Substitute] > 0 next true if target.has_active_ability?(:STICKYHOLD) next true if battle.corrosiveGas[target.index % 2][target.party_index] diff --git a/Data/Scripts/022_Maruno/debug battle tests.rb b/Data/Scripts/022_Maruno/debug battle tests.rb index 7ec4167e9..d6a0d4e61 100644 --- a/Data/Scripts/022_Maruno/debug battle tests.rb +++ b/Data/Scripts/022_Maruno/debug battle tests.rb @@ -1,9 +1,93 @@ -# TODO: Better randomisation of moves, including tracking of how many times each -# function code has been tested (note that some Pokémon may not be used in -# battle, so their moves won't be score). -# TODO: Add held items. +# TODO: Be much more relevant with the choosing of held items. For example, +# only give a weather-boosting item to a Pokémon that can create the +# corresponding weather. +# TODO: Add alternate forms, which will give access to more abilities. Note that +# some alternate forms require fusion or specific held items or other +# things. -AI_MOVE_TESTING_THRESHOLD = 100 +AI_MOVE_TESTING_THRESHOLD = 500 +AI_ABILITY_TESTING_THRESHOLD = 100 +AI_ITEM_TESTING_THRESHOLD = 100 + +#=============================================================================== +# +#=============================================================================== +# There are some duplicate effects in here (e.g. Power Weight/Bracer/etc.), but +# whatever. +ITEMS_WITH_HELD_EFFECTS = [ + :AIRBALLOON, :BRIGHTPOWDER, :EVIOLITE, :FLOATSTONE, :DESTINYKNOT, + :ROCKYHELMET, :ASSAULTVEST, :SAFETYGOGGLES, :PROTECTIVEPADS, :HEAVYDUTYBOOTS, + :UTILITYUMBRELLA, :EJECTBUTTON, :EJECTPACK, :REDCARD, :SHEDSHELL, :CHOICEBAND, + :CHOICESPECS, :CHOICESCARF, :HEATROCK, :DAMPROCK, :SMOOTHROCK, :ICYROCK, + :TERRAINEXTENDER, :LIGHTCLAY, :GRIPCLAW, :BINDINGBAND, :BIGROOT, :BLACKSLUDGE, + :LEFTOVERS, :SHELLBELL, :MENTALHERB, :WHITEHERB, :POWERHERB, :ABSORBBULB, + :CELLBATTERY, :LUMINOUSMOSS, :SNOWBALL, :WEAKNESSPOLICY, :BLUNDERPOLICY, + :THROATSPRAY, :ADRENALINEORB, :ROOMSERVICE, :ELECTRICSEED, :GRASSYSEED, + :MISTYSEED, :PSYCHICSEED, :LIFEORB, :EXPERTBELT, :METRONOME, :MUSCLEBAND, + :WISEGLASSES, :RAZORCLAW, :SCOPELENS, :WIDELENS, :ZOOMLENS, :KINGSROCK, + :RAZORFANG, :LAGGINGTAIL, :QUICKCLAW, :FOCUSBAND, :FOCUSSASH, :FLAMEORB, + :TOXICORB, :STICKYBARB, :IRONBALL, :RINGTARGET, :MACHOBRACE, :POWERWEIGHT, + :POWERBRACER, :POWERBELT, :POWERLENS, :POWERBAND, :POWERANKLET, :LAXINCENSE, + :FULLINCENSE, :SEAINCENSE, :WAVEINCENSE, :ROSEINCENSE, :ODDINCENSE, + :ROCKINCENSE, :CHARCOAL, :MYSTICWATER, :MAGNET, :MIRACLESEED, :NEVERMELTICE, + :BLACKBELT, :POISONBARB, :SOFTSAND, :SHARPBEAK, :TWISTEDSPOON, :SILVERPOWDER, + :HARDSTONE, :SPELLTAG, :DRAGONFANG, :BLACKGLASSES, :METALCOAT, :SILKSCARF, + :FIREGEM, :WATERGEM, :ELECTRICGEM, :GRASSGEM, :ICEGEM, :FIGHTINGGEM, + :POISONGEM, :GROUNDGEM, :FLYINGGEM, :PSYCHICGEM, :BUGGEM, :ROCKGEM, :GHOSTGEM, + :DRAGONGEM, :DARKGEM, :STEELGEM, :FAIRYGEM, :NORMALGEM, :CHERIBERRY, + :CHESTOBERRY, :PECHABERRY, :RAWSTBERRY, :ASPEARBERRY, :LEPPABERRY, :ORANBERRY, + :PERSIMBERRY, :LUMBERRY, :SITRUSBERRY, :FIGYBERRY, :WIKIBERRY, :MAGOBERRY, + :AGUAVBERRY, :IAPAPABERRY, :OCCABERRY, :PASSHOBERRY, :WACANBERRY, :RINDOBERRY, + :YACHEBERRY, :CHOPLEBERRY, :KEBIABERRY, :SHUCABERRY, :COBABERRY, :PAYAPABERRY, + :TANGABERRY, :CHARTIBERRY, :KASIBBERRY, :HABANBERRY, :COLBURBERRY, + :BABIRIBERRY, :ROSELIBERRY, :CHILANBERRY, :LIECHIBERRY, :GANLONBERRY, + :SALACBERRY, :PETAYABERRY, :APICOTBERRY, :LANSATBERRY, :STARFBERRY, + :ENIGMABERRY, :MICLEBERRY, :CUSTAPBERRY, :JABOCABERRY, :ROWAPBERRY, :KEEBERRY, + :MARANGABERRY +] +# These items have no effect if held by other species. Includes the Plates +# because other items also have the type-boosting effect and we don't need to +# test that effect of Plates. +SIGNATURE_ITEMS = { + :PIKACHU => :LIGHTBALL, + :CHANSEY => :LUCKYPUNCH, + :DITTO => [:METALPOWDER, :QUICKPOWDER], + :CUBONE => :THICKCLUB, + :MAROWAK => :THICKCLUB, + :FARFETCHD => :LEEK, + :SIRFETCHD => :LEEK, + :LATIOS => :SOULDEW, + :LATIAS => :SOULDEW, + :CLAMPERL => [:DEEPSEATOOTH, :DEEPSEASCALE], + :DIALGA => :ADAMANTORB, + :PALKIA => :LUSTROUSORB, + :ARCEUS => [:FLAMEPLATE, :SPLASHPLATE, :ZAPPLATE, :MEADOWPLATE, + :ICICLEPLATE, :FISTPLATE, :TOXICPLATE, :EARTHPLATE, :SKYPLATE, + :MINDPLATE, :INSECTPLATE, :STONEPLATE, :SPOOKYPLATE, + :DRACOPLATE, :DREADPLATE, :IRONPLATE, :PIXIEPLATE], + :GENESECT => [:DOUSEDRIVE, :SHOCKDRIVE, :BURNDRIVE, :CHILLDRIVE], + :SILVALLY => [:FIREMEMORY, :WATERMEMORY, :ELECTRICMEMORY, :GRASSMEMORY, + :ICEMEMORY, :FIGHTINGMEMORY, :POISONMEMORY, :GROUNDMEMORY, + :FLYINGMEMORY, :PSYCHICMEMORY, :BUGMEMORY, :ROCKMEMORY, + :GHOSTMEMORY, :DRAGONMEMORY, :DARKMEMORY, :STEELMEMORY, + :FAIRYMEMORY], + :GROUDON => :REDORB, # Form-changing item + :KYOGRE => :BLUEORB, # Form-changing item + :GIRATINA => :GRISEOUSORB, # Form-changing item + :ZACIAN => :RUSTEDSWORD, # Form-changing item + :ZAMAZENTA => :RUSTEDSHIELD # Form-changing item +} +MEGA_STONES = [ + :VENUSAURITE, :CHARIZARDITEX, :CHARIZARDITEY, :BLASTOISINITE, :BEEDRILLITE, + :PIDGEOTITE, :ALAKAZITE, :SLOWBRONITE, :GENGARITE, :KANGASKHANITE, :PINSIRITE, + :GYARADOSITE, :AERODACTYLITE, :MEWTWONITEX, :MEWTWONITEY, :AMPHAROSITE, + :STEELIXITE, :SCIZORITE, :HERACRONITE, :HOUNDOOMINITE, :TYRANITARITE, + :SCEPTILITE, :BLAZIKENITE, :SWAMPERTITE, :GARDEVOIRITE, :SABLENITE, :MAWILITE, + :AGGRONITE, :MEDICHAMITE, :MANECTITE, :SHARPEDONITE, :CAMERUPTITE, + :ALTARIANITE, :BANETTITE, :ABSOLITE, :GLALITITE, :SALAMENCITE, :METAGROSSITE, + :LATIASITE, :LATIOSITE, :LOPUNNITE, :GARCHOMPITE, :LUCARIONITE, :ABOMASITE, + :GALLADITE, :AUDINITE, :DIANCITE +] #=============================================================================== # @@ -27,6 +111,7 @@ def debug_set_up_trainer trainer = NPCTrainer.new(trainer_name, trainer_type) trainer.id = $player.make_foreign_ID trainer.lose_text = "I lost." + trainer.items.push(:MEGARING) # [:MAXPOTION, :FULLHEAL, :MAXREVIVE, :REVIVE].each do |item| # trainer.items.push(item) # end @@ -40,20 +125,57 @@ def debug_set_up_trainer this_species = valid_species.sample this_level = 100 # rand(1, Settings::MAXIMUM_LEVEL) pkmn = Pokemon.new(this_species, this_level, trainer, false) + # Generate moveset for pkmn (from level-up moves first, then from tutor + # moves + egg moves, then from all moves) all_moves = pkmn.getMoveList.map { |m| m[1] } all_moves.uniq! all_moves.reject! { |m| $tested_moves[m] && $tested_moves[m] > AI_MOVE_TESTING_THRESHOLD } if all_moves.length == 0 - all_moves = GameData::Move.keys + all_moves = pkmn.species_data.tutor_moves.clone + pkmn.species_data.get_egg_moves.clone all_moves.reject! { |m| $tested_moves[m] && $tested_moves[m] > AI_MOVE_TESTING_THRESHOLD } + if all_moves.length == 0 + all_moves = GameData::Move.keys.clone + all_moves.reject! { |m| $tested_moves[m] && $tested_moves[m] > AI_MOVE_TESTING_THRESHOLD } + end end - if all_moves.length == 0 + if all_moves.length == 0 && !$shown_all_moves_tested_message echoln "All moves have been tested at least #{AI_MOVE_TESTING_THRESHOLD} times!" + $shown_all_moves_tested_message = true end - all_moves = pkmn.getMoveList.map { |m| m[1] } - all_moves.uniq! moves = all_moves.sample(4) moves.each { |m| pkmn.learn_move(m) } + # Generate held item for pkmn (compatible Mega Stone first, then compatible + # signature item, then any item with a held effect) + all_items = [] # Find all compatible Mega Stones + GameData::Species.each do |sp| + next if sp.species != pkmn.species || sp.unmega_form != pkmn.form + all_items.push(sp.mega_stone) if sp.mega_stone + end + all_items.reject! { |i| $tested_items[i] && $tested_items[i] > AI_ITEM_TESTING_THRESHOLD } + if all_items.length > 0 && rand(100) < 50 + pkmn.item = all_items.sample + elsif SIGNATURE_ITEMS.keys.include?(pkmn.species) && rand(100) < 75 + all_items = SIGNATURE_ITEMS[pkmn.species].clone + if all_items.is_a?(Array) + all_items.reject! { |i| $tested_items[i] && $tested_items[i] > AI_ITEM_TESTING_THRESHOLD } + pkmn.item = all_items.sample if all_items.length > 0 + else + pkmn.item = all_items if !$tested_items[all_items] || $tested_items[all_items] <= AI_ITEM_TESTING_THRESHOLD + end + end + if !pkmn.hasItem? && rand(100) < 75 + all_items = ITEMS_WITH_HELD_EFFECTS.clone + all_items.reject! { |i| $tested_items[i] && $tested_items[i] > AI_ITEM_TESTING_THRESHOLD } + all_items = ITEMS_WITH_HELD_EFFECTS.clone if all_items.length == 0 + pkmn.item = all_items.sample + end + # Generate ability for pkmn (any available to that species/form) + abil = pkmn.ability_id + if $tested_abilities[abil] && $tested_abilities[abil] > AI_ABILITY_TESTING_THRESHOLD + abils = pkmn.getAbilityList + abils.reject! { |a| $tested_abilities[a[0]] && $tested_abilities[a[0]] > AI_ABILITY_TESTING_THRESHOLD } + pkmn.ability_index = abils.sample[1] if abils.length > 0 + end trainer.party.push(pkmn) pokemon_array.push(pkmn) end @@ -62,8 +184,11 @@ def debug_set_up_trainer return trainer_array, foe_items, pokemon_array, party_starts end +#=============================================================================== +# +#=============================================================================== def debug_test_auto_battle(logging = false, console_messages = true) - mar_load_tested_moves_record + mar_load_tested_data_record old_internal = $INTERNAL $INTERNAL = logging @@ -131,24 +256,53 @@ def debug_test_auto_battle(logging = false, console_messages = true) end $INTERNAL = old_internal - mar_save_tested_moves_record + mar_save_tested_data_record end -def mar_load_tested_moves_record - return if $tested_moves - pbRgssOpen("tested_moves.dat", "rb") { |f| $tested_moves = Marshal.load(f) } +def mar_load_tested_data_record + if !$tested_moves + pbRgssOpen("tested_moves.dat", "rb") { |f| $tested_moves = Marshal.load(f) } + end + if !$tested_abilities + pbRgssOpen("tested_abilities.dat", "rb") { |f| $tested_abilities = Marshal.load(f) } + end + if !$tested_items + pbRgssOpen("tested_items.dat", "rb") { |f| $tested_items = Marshal.load(f) } + end end -def mar_save_tested_moves_record - File.open("tested_moves.dat", "wb") { |f| Marshal.dump($tested_moves || {}, f) } +def mar_save_tested_data_record + $tested_moves ||= {} + $tested_abilities ||= {} + $tested_items ||= {} + File.open("tested_moves.dat", "wb") { |f| Marshal.dump($tested_moves, f) } + File.open("tested_abilities.dat", "wb") { |f| Marshal.dump($tested_abilities, f) } + File.open("tested_items.dat", "wb") { |f| Marshal.dump($tested_items, f) } end #=============================================================================== # Add to Debug menu. #=============================================================================== +MenuHandlers.add(:debug_menu, :ai_testing_menu, { + "name" => "AI Testing...", + "parent" => :main, + "description" => "Functions that help to test the AI.", + "always_show" => false +}) + +MenuHandlers.add(:debug_menu, :generate_test_data, { + "name" => "Generate new test data", + "parent" => :ai_testing_menu, + "description" => "Save current tested moves/abilities/items data. If none, generates it from scratch.", + "always_show" => false, + "effect" => proc { + mar_save_tested_data_record + } +}) + MenuHandlers.add(:debug_menu, :test_auto_battle, { "name" => "Test Auto Battle", - "parent" => :main, + "parent" => :ai_testing_menu, "description" => "Runs an AI-controlled battle with no visuals.", "always_show" => false, "effect" => proc { @@ -157,8 +311,8 @@ MenuHandlers.add(:debug_menu, :test_auto_battle, { }) MenuHandlers.add(:debug_menu, :test_auto_battle_logging, { - "name" => "Test Auto Battle with Logging", - "parent" => :main, + "name" => "Test Auto Battle with logging", + "parent" => :ai_testing_menu, "description" => "Runs an AI-controlled battle with no visuals. Logs messages.", "always_show" => false, "effect" => proc { @@ -168,8 +322,8 @@ MenuHandlers.add(:debug_menu, :test_auto_battle_logging, { }) MenuHandlers.add(:debug_menu, :bulk_test_auto_battle, { - "name" => "Bulk Test Auto Battle", - "parent" => :main, + "name" => "Bulk test Auto Battle", + "parent" => :ai_testing_menu, "description" => "Runs 50 AI-controlled battles with no visuals.", "always_show" => false, "effect" => proc { @@ -182,23 +336,19 @@ MenuHandlers.add(:debug_menu, :bulk_test_auto_battle, { } }) -MenuHandlers.add(:debug_menu, :load_tested_moves, { - "name" => "Load tested moves", - "parent" => :main, - "description" => "Load tested moves", +#=============================================================================== +# +#=============================================================================== +MenuHandlers.add(:debug_menu, :review_tested_data, { + "name" => "Review tested moves/abilities/items", + "parent" => :ai_testing_menu, + "description" => "List how much all moves/abilities/items have been tested.", "always_show" => false, "effect" => proc { - mar_load_tested_moves_record - } -}) + mar_load_tested_data_record -MenuHandlers.add(:debug_menu, :review_tested_moves, { - "name" => "Review tested moves", - "parent" => :main, - "description" => "List all tested moves and how much they have been tested.", - "always_show" => false, - "effect" => proc { - mar_save_tested_moves_record + max_move_length = 0 + GameData::Move.keys.each { |m| max_move_length = [m.to_s.length, max_move_length].max } thresholded_moves = [] ($tested_moves || {}).each_pair do |move, count| next if !count || count < AI_MOVE_TESTING_THRESHOLD @@ -217,7 +367,10 @@ MenuHandlers.add(:debug_menu, :review_tested_moves, { f.write("Met threshold of #{AI_MOVE_TESTING_THRESHOLD}: #{thresholded_moves.length}\r\n") f.write("================================================\r\n") thresholded_moves.each do |m| - f.write("#{m[0]} = #{m[1]}\r\n") + f.write(m[0].to_s) + f.write(" " * (5 + max_move_length - m[0].to_s.length)) + f.write(m[1].to_s) + f.write("\r\n") end f.write("\r\n") f.write("\r\n") @@ -226,14 +379,114 @@ MenuHandlers.add(:debug_menu, :review_tested_moves, { f.write("Remaining moves: #{remaining_moves.length}\r\n") f.write("================================================\r\n") remaining_moves.each do |m| + f.write(m.to_s) if $tested_moves && $tested_moves[m] - f.write("#{m} = #{$tested_moves[m]}\r\n") - else - f.write("#{m}\r\n") + f.write(" " * (5 + max_move_length - m.to_s.length)) + f.write($tested_moves[m].to_s) end + f.write("\r\n") end end + echoln "Moves: #{thresholded_moves.length} tested at least #{AI_MOVE_TESTING_THRESHOLD} times." + echoln " #{remaining_moves.length} moves need more testing." - echoln "Done." + #--------------------------------------------------------------------------- + + max_ability_length = 0 + GameData::Ability.keys.each { |a| max_ability_length = [a.to_s.length, max_ability_length].max } + thresholded_abilities = [] + ($tested_abilities || {}).each_pair do |abil, count| + next if !count || count < AI_ABILITY_TESTING_THRESHOLD + thresholded_abilities.push([abil, count]) + end + thresholded_abilities.sort! { |a, b| a[0].to_s <=> b[0].to_s } + remaining_abilities = GameData::Ability.keys.clone + thresholded_abilities.each { |a| remaining_abilities.delete(a[0]) } + remaining_abilities.sort! { |a, b| a.to_s <=> b.to_s } + + File.open("tested abilities summary.txt", "wb") do |f| + f.write(0xEF.chr) + f.write(0xBB.chr) + f.write(0xBF.chr) + f.write("================================================\r\n") + f.write("Met threshold of #{AI_ABILITY_TESTING_THRESHOLD}: #{thresholded_abilities.length}\r\n") + f.write("================================================\r\n") + thresholded_abilities.each do |a| + f.write(a[0].to_s) + f.write(" " * (5 + max_ability_length - a[0].to_s.length)) + f.write(a[1].to_s) + f.write("\r\n") + end + f.write("\r\n") + f.write("\r\n") + f.write("\r\n") + f.write("================================================\r\n") + f.write("Remaining abilities: #{remaining_abilities.length}\r\n") + f.write("================================================\r\n") + remaining_abilities.each do |a| + f.write(a.to_s) + if $tested_abilities && $tested_abilities[a] + f.write(" " * (5 + max_ability_length - a.to_s.length)) + f.write($tested_abilities[a].to_s) + end + f.write("\r\n") + end + end + echoln "Abilities: #{thresholded_abilities.length} tested at least #{AI_ABILITY_TESTING_THRESHOLD} times." + echoln " #{remaining_abilities.length} abilities need more testing." + + #--------------------------------------------------------------------------- + + max_item_length = 0 + ITEMS_WITH_HELD_EFFECTS.each { |i| max_item_length = [i.to_s.length, max_item_length].max } + thresholded_items = [] + ($tested_items || {}).each_pair do |item, count| + next if !count || count < AI_ITEM_TESTING_THRESHOLD + thresholded_items.push([item, count]) + end + thresholded_items.sort! { |a, b| a[0].to_s <=> b[0].to_s } + remaining_items = ITEMS_WITH_HELD_EFFECTS.clone + remaining_items += MEGA_STONES.clone + SIGNATURE_ITEMS.each_pair do |species, items| + if items.is_a?(Array) + remaining_items += items + else + remaining_items.push(items) + end + end + remaining_items.uniq! + thresholded_items.each { |i| remaining_items.delete(i[0]) } + remaining_items.sort! { |a, b| a.to_s <=> b.to_s } + + File.open("tested items summary.txt", "wb") do |f| + f.write(0xEF.chr) + f.write(0xBB.chr) + f.write(0xBF.chr) + f.write("================================================\r\n") + f.write("Met threshold of #{AI_ITEM_TESTING_THRESHOLD}: #{thresholded_items.length}\r\n") + f.write("================================================\r\n") + thresholded_items.each do |i| + f.write(i[0].to_s) + f.write(" " * (5 + max_item_length - i[0].to_s.length)) + f.write(i[1].to_s) + f.write("\r\n") + end + f.write("\r\n") + f.write("\r\n") + f.write("\r\n") + f.write("================================================\r\n") + f.write("Remaining items: #{remaining_items.length}\r\n") + f.write("================================================\r\n") + remaining_items.each do |i| + f.write(i.to_s) + if $tested_items && $tested_items[i] + f.write(" " * (5 + max_item_length - i.to_s.length)) + f.write($tested_items[i].to_s) + end + f.write("\r\n") + end + end + echoln "Items: #{thresholded_items.length} tested at least #{AI_ITEM_TESTING_THRESHOLD} times." + echoln " #{remaining_items.length} items need more testing." } })