Added Marin's AI as a reference

This commit is contained in:
Maruno17
2022-08-10 18:38:20 +01:00
parent eeeb9b3494
commit 5ff0c04eed
5 changed files with 3217 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,209 @@
class PokeBattle_Battle
attr_reader :battleAI
alias mkai_initialize initialize
def initialize(*args)
mkai_initialize(*args)
@battleAI = MKAI.new(self, self.wildBattle?)
@battleAI.sides[0].set_party(@party1)
@battleAI.sides[0].set_trainers(@player)
@battleAI.sides[1].set_party(@party2)
@battleAI.sides[1].set_trainers(@opponent)
end
def pbRecallAndReplace(idxBattler, idxParty, batonPass = false)
if !@battlers[idxBattler].fainted?
@scene.pbRecall(idxBattler)
@battleAI.sides[idxBattler % 2].recall(idxBattler)
end
@battlers[idxBattler].pbAbilitiesOnSwitchOut # Inc. primordial weather check
@scene.pbShowPartyLineup(idxBattler & 1) if pbSideSize(idxBattler) == 1
pbMessagesOnReplace(idxBattler, idxParty)
pbReplace(idxBattler, idxParty, batonPass)
end
# Bug fix (used b instead of battler)
def pbMessageOnRecall(battler)
if battler.pbOwnedByPlayer?
if battler.hp<=battler.totalhp/4
pbDisplayBrief(_INTL("Good job, {1}! Come back!",battler.name))
elsif battler.hp<=battler.totalhp/2
pbDisplayBrief(_INTL("OK, {1}! Come back!",battler.name))
elsif battler.turnCount>=5
pbDisplayBrief(_INTL("{1}, thats enough! Come back!",battler.name))
elsif battler.turnCount>=2
pbDisplayBrief(_INTL("{1}, come back!",battler.name))
else
pbDisplayBrief(_INTL("{1}, switch out! Come back!",battler.name))
end
else
owner = pbGetOwnerName(battler.index)
pbDisplayBrief(_INTL("{1} withdrew {2}!",owner,battler.name))
end
end
alias mkai_pbEndOfRoundPhase pbEndOfRoundPhase
def pbEndOfRoundPhase
mkai_pbEndOfRoundPhase
@battleAI.end_of_round
end
alias mkai_pbShowAbilitySplash pbShowAbilitySplash
def pbShowAbilitySplash(battler, delay = false, logTrigger = true)
mkai_pbShowAbilitySplash(battler, delay, logTrigger)
@battleAI.reveal_ability(battler)
end
end
class PokeBattle_Move
attr_reader :statUp
attr_reader :statDown
alias mkai_pbReduceDamage pbReduceDamage
def pbReduceDamage(user, target)
mkai_pbReduceDamage(user, target)
@battle.battleAI.register_damage(self, user, target, target.damageState.hpLost)
end
def pbCouldBeCritical?(user, target)
return false if target.pbOwnSide.effects[PBEffects::LuckyChant] > 0
# Set up the critical hit ratios
ratios = (NEWEST_BATTLE_MECHANICS) ? [24,8,2,1] : [16,8,4,3,2]
c = 0
# Ability effects that alter critical hit rate
if c >= 0 && user.abilityActive?
c = BattleHandlers.triggerCriticalCalcUserAbility(user.ability, user, target, c)
end
if c >= 0 && target.abilityActive? && !@battle.moldBreaker
c = BattleHandlers.triggerCriticalCalcTargetAbility(target.ability, user, target, c)
end
# Item effects that alter critical hit rate
if c >= 0 && user.itemActive?
c = BattleHandlers.triggerCriticalCalcUserItem(user.item, user, target, c)
end
if c >= 0 && target.itemActive?
c = BattleHandlers.triggerCriticalCalcTargetItem(target.item, user, target, c)
end
return false if c < 0
# Move-specific "always/never a critical hit" effects
return false if pbCritialOverride(user,target) == -1
return true
end
end
class MKAI
def pbAIRandom(x)
return rand(x)
end
def pbDefaultChooseEnemyCommand(idxBattler)
sideIndex = idxBattler % 2
index = MKAI.battler_to_proj_index(idxBattler)
side = @sides[sideIndex]
projection = side.battlers[index]
# Choose move
data = projection.choose_move
if data.nil?
# Struggle
@battle.pbAutoChooseMove(idxBattler)
elsif data[0] == :ITEM
# [:ITEM, item_id, target&]
item = data[1]
# Determine target of item (always the Pokémon choosing the action)
useType = pbGetItemData(item, ITEM_BATTLE_USE)
if data[2]
target_index = data[2]
else
target_index = idxBattler
if useType && (useType == 1 || useType == 6) # Use on Pokémon
target_index = @battle.battlers[target_index].pokemonIndex # Party Pokémon
end
end
# Register our item
@battle.pbRegisterItem(idxBattler, item, target_index)
elsif data[0] == :SWITCH
# [:SWITCH, pokemon_index]
@battle.pbRegisterSwitch(idxBattler, data[1])
else
# [move_index, move_target]
move_index, move_target = data
# Mega evolve if we determine that we should
@battle.pbRegisterMegaEvolution(idxBattler) if projection.should_mega_evolve?(idxBattler)
# Register our move
@battle.pbRegisterMove(idxBattler, move_index, false)
# Register the move's target
@battle.pbRegisterTarget(idxBattler, move_target)
end
end
#=============================================================================
# Choose a replacement Pokémon
#=============================================================================
def pbDefaultChooseNewEnemy(idxBattler, party)
proj = self.battler_to_projection(@battle.battlers[idxBattler])
scores = proj.get_optimal_switch_choice
scores.each do |_, _, proj|
pkmn = proj.pokemon
index = @battle.pbParty(idxBattler).index(pkmn)
if @battle.pbCanSwitchLax?(idxBattler, index)
return index
end
end
return -1
end
end
class PokeBattle_Battler
alias mkai_pbInitialize pbInitialize
def pbInitialize(pkmn, idxParty, batonPass = false)
mkai_pbInitialize(pkmn, idxParty, batonPass)
ai = @battle.battleAI
sideIndex = @index % 2
ai.sides[sideIndex].send_out(@index, self)
end
alias mkai_pbFaint pbFaint
def pbFaint(*args)
mkai_pbFaint(*args)
@battle.battleAI.faint_battler(self)
end
end
class PokeBattle_PoisonMove
attr_reader :toxic
end
class Array
def sum
n = 0
self.each { |e| n += e }
n
end
end
# Overwrite Frisk to show the enemy held item
BattleHandlers::AbilityOnSwitchIn.add(:FRISK,
proc { |ability,battler,battle|
foes = []
battle.eachOtherSideBattler(battler.index) do |b|
foes.push(b) if b.item > 0
end
if foes.length > 0
battle.pbShowAbilitySplash(battler)
if NEWEST_BATTLE_MECHANICS
foes.each do |b|
battle.pbDisplay(_INTL("{1} frisked {2} and found its {3}!",
battler.pbThis, b.pbThis(true), PBItems.getName(b.item)))
battle.battleAI.reveal_item(b)
end
else
foe = foes[battle.pbRandom(foes.length)]
battle.pbDisplay(_INTL("{1} frisked the foe and found one {2}!",
battler.pbThis, PBItems.getName(foe.item)))
battle.battleAI.reveal_item(foe)
end
battle.pbHideAbilitySplash(battler)
end
}
)

View File

@@ -0,0 +1,133 @@
class MKAI
attr_reader :battle
attr_reader :sides
def initialize(battle, wild_battle)
@battle = battle
@sides = [Side.new(self, 0), Side.new(self, 1, wild_battle)]
MKAI.log("AI initialized")
end
def self.battler_to_proj_index(battlerIndex)
if battlerIndex % 2 == 0 # Player side: 0, 2, 4 -> 0, 1, 2
return battlerIndex / 2
else # Opponent side: 1, 3, 5 -> 0, 1, 2
return (battlerIndex - 1) / 2
end
end
def self.weighted_rand(weights)
num = rand(weights.sum)
for i in 0...weights.size
if num < weights[i]
return i
else
num -= weights[i]
end
end
return nil
end
def self.get_weights(factor, weights)
avg = weights.sum / weights.size.to_f
newweights = weights.map do |e|
diff = e - avg
next [0, ((e - diff * factor) * 100).round].max
end
return newweights
end
def self.weighted_factored_rand(factor, weights)
avg = weights.sum / weights.size.to_f
newweights = weights.map do |e|
diff = e - avg
next [0, ((e - diff * factor) * 100).round].max
end
return weighted_rand(newweights)
end
def self.log(msg)
echoln msg
end
def battler_to_projection(battler)
@sides.each do |side|
side.battlers.each do |projection|
if projection && projection.pokemon == battler.pokemon
return projection
end
end
side.party.each do |projection|
if projection && projection.pokemon == battler.pokemon
return projection
end
end
end
return nil
end
def pokemon_to_projection(pokemon)
@sides.each do |side|
side.battlers.each do |projection|
if projection && projection.pokemon == pokemon
return projection
end
end
side.party.each do |projection|
if projection && projection.pokemon == pokemon
return projection
end
end
end
return nil
end
def register_damage(move, user, target, damage)
user = battler_to_projection(user)
target = battler_to_projection(target)
user.register_damage_dealt(move, target, damage)
target.register_damage_taken(move, user, damage)
end
def faint_battler(battler)
# Remove the battler from the AI's list of the active battlers
@sides.each do |side|
side.battlers.each_with_index do |proj, index|
if proj && proj.battler == battler
# Decouple the projection from the battler
side.recall(battler.index)
side.battlers[index] = nil
break
end
end
end
end
def end_of_round
@sides.each { |side| side.end_of_round }
end
def reveal_ability(battler)
@sides.each do |side|
side.battlers.each do |proj|
if proj && proj.battler == battler && !proj.revealed_ability
proj.revealed_ability = true
MKAI.log("#{proj.pokemon.name}'s ability was revealed.")
break
end
end
end
end
def reveal_item(battler)
@sides.each do |side|
side.battlers.each do |proj|
if proj.battler == battler && !proj.revealed_item
proj.revealed_item = true
MKAI.log("#{proj.pokemon.name}'s item was revealed.")
break
end
end
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
class MKAI
class Side
attr_reader :ai
attr_reader :index
attr_reader :battlers
attr_reader :party
attr_reader :trainers
attr_reader :flags
def initialize(ai, index, wild_pokemon = false)
@ai = ai
@index = index
@battle = @ai.battle
@wild_pokemon = wild_pokemon
@battlers = []
@party = []
@flags = {}
end
def effects
return @battle.sides[@index].effects
end
def set_party(party)
@party = party.map { |pokemon| BattlerProjection.new(self, pokemon, @wild_pokemon) }
end
def set_trainers(trainers)
@trainers = trainers
end
def opposing_side
return @ai.sides[1 - @index]
end
def recall(battlerIndex)
index = MKAI.battler_to_proj_index(battlerIndex)
proj = @battlers[index]
if proj.nil?
raise "Battler to be recalled was not found in the active battlers list."
end
if !proj.active?
raise "Battler to be recalled was not active."
end
@battlers[index] = nil
proj.battler = nil
end
def send_out(battlerIndex, battler)
proj = @party.find { |proj| proj && proj.pokemon == battler.pokemon }
if proj.nil?
raise "Battler to be sent-out was not found in the party list."
end
if proj.active?
raise "Battler to be sent-out was already sent out before."
end
index = MKAI.battler_to_proj_index(battlerIndex)
@battlers[index] = proj
proj.ai_index = index
proj.battler = battler
end
def end_of_round
@battlers.each { |proj| proj.end_of_round if proj }
@flags = {}
end
end
end