mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-10 14:44:58 +00:00
Rearranged OrgBattle scripts
This commit is contained in:
@@ -0,0 +1,344 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class BaseStatRestriction
|
||||
def initialize(mn, mx)
|
||||
@mn = mn
|
||||
@mx = mx
|
||||
end
|
||||
|
||||
def isValid?(pkmn)
|
||||
bst = baseStatTotal(pkmn.species)
|
||||
return bst >= @mn && bst <= @mx
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class NonlegendaryRestriction
|
||||
def isValid?(pkmn)
|
||||
return true if !pkmn.genderless?
|
||||
return false if pkmn.species_data.egg_groups.include?(:Undiscovered)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class InverseRestriction
|
||||
def initialize(r)
|
||||
@r = r
|
||||
end
|
||||
|
||||
def isValid?(pkmn)
|
||||
return !@r.isValid?(pkmn)
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
=begin
|
||||
[3/10]
|
||||
0-266 - 0-500
|
||||
[106]
|
||||
267-372 - 380-500
|
||||
[95]
|
||||
373-467 - 400-555 (nonlegendary)
|
||||
468-563 - 400-555 (nonlegendary)
|
||||
564-659 - 400-555 (nonlegendary)
|
||||
660-755 - 400-555 (nonlegendary)
|
||||
756-799 - 580-600 [legendary] (compat1==15 or compat2==15, genderbyte=255)
|
||||
800-849 - 500-
|
||||
850-881 - 580-
|
||||
=end
|
||||
|
||||
def withRestr(_rule, minbs, maxbs, legendary)
|
||||
ret = PokemonChallengeRules.new.addPokemonRule(BaseStatRestriction.new(minbs, maxbs))
|
||||
if legendary == 0
|
||||
ret.addPokemonRule(NonlegendaryRestriction.new)
|
||||
elsif legendary == 1
|
||||
ret.addPokemonRule(InverseRestriction.new(NonlegendaryRestriction.new))
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbArrangeByTier(pokemonlist, rule)
|
||||
tiers = [
|
||||
withRestr(rule, 0, 500, 0),
|
||||
withRestr(rule, 380, 500, 0),
|
||||
withRestr(rule, 400, 555, 0),
|
||||
withRestr(rule, 400, 555, 0),
|
||||
withRestr(rule, 400, 555, 0),
|
||||
withRestr(rule, 400, 555, 0),
|
||||
withRestr(rule, 580, 680, 1),
|
||||
withRestr(rule, 500, 680, 0),
|
||||
withRestr(rule, 580, 680, 2)
|
||||
]
|
||||
tierPokemon = []
|
||||
tiers.length.times do
|
||||
tierPokemon.push([])
|
||||
end
|
||||
# Sort each Pokémon into tiers. Which tier a Pokémon is put in deoends on the
|
||||
# Pokémon's position within pokemonlist (later = higher tier). pokemonlist is
|
||||
# already roughly arranged by rank from weakest to strongest.
|
||||
for i in 0...pokemonlist.length
|
||||
next if !rule.ruleset.isPokemonValid?(pokemonlist[i])
|
||||
validtiers = []
|
||||
for j in 0...tiers.length
|
||||
validtiers.push(j) if tiers[j].ruleset.isPokemonValid?(pokemonlist[i])
|
||||
end
|
||||
if validtiers.length > 0
|
||||
vt = validtiers.length * i / pokemonlist.length
|
||||
tierPokemon[validtiers[vt]].push(pokemonlist[i])
|
||||
end
|
||||
end
|
||||
# Now for each tier, sort the Pokemon in that tier by their BST (lowest first).
|
||||
ret = []
|
||||
for i in 0...tiers.length
|
||||
tierPokemon[i].sort! { |a, b|
|
||||
bstA = baseStatTotal(a.species)
|
||||
bstB = baseStatTotal(b.species)
|
||||
(bstA == bstB) ? a.species <=> b.species : bstA <=> bstB
|
||||
}
|
||||
ret.concat(tierPokemon[i])
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
def pbReplenishBattlePokemon(party, rule)
|
||||
while party.length < 20
|
||||
pkmn = pbRandomPokemonFromRule(rule, nil)
|
||||
found = false
|
||||
for pk in party
|
||||
next if !isBattlePokemonDuplicate(pkmn, pk)
|
||||
found = true
|
||||
break
|
||||
end
|
||||
party.push(pkmn) if !found
|
||||
end
|
||||
end
|
||||
|
||||
def isBattlePokemonDuplicate(pk, pk2)
|
||||
return false if pk.species != pk2.species
|
||||
moves1 = []
|
||||
moves2 = []
|
||||
for i in 0...Pokemon::MAX_MOVES
|
||||
moves1.push((pk.moves[i]) ? pk.moves[i].id : nil)
|
||||
moves2.push((pk2.moves[i]) ? pk2.moves[i].id : nil)
|
||||
end
|
||||
moves1.sort!
|
||||
moves2.sort!
|
||||
# Accept as same if moves are same and there are four moves each
|
||||
return true if moves1 == moves2 && moves1[Pokemon::MAX_MOVES - 1]
|
||||
same_evs = true
|
||||
GameData::Stat.each_main { |s| same_evs = false if pk.ev[s.id] != pk2.ev[s.id] }
|
||||
return pk.item_id == pk2.item_id && pk.nature_id == pk2.nature_id && same_evs
|
||||
end
|
||||
|
||||
def pbRemoveDuplicates(party)
|
||||
ret = []
|
||||
for pk in party
|
||||
found = false
|
||||
count = 0
|
||||
firstIndex = -1
|
||||
for i in 0...ret.length
|
||||
pk2 = ret[i]
|
||||
if isBattlePokemonDuplicate(pk, pk2)
|
||||
found = true
|
||||
break
|
||||
end
|
||||
if pk.species == pk2.species
|
||||
firstIndex = i if count == 0
|
||||
count += 1
|
||||
end
|
||||
end
|
||||
if !found
|
||||
ret.delete_at(firstIndex) if count >= 10
|
||||
ret.push(pk)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
def pbGenerateChallenge(rule, tag)
|
||||
oldrule = rule
|
||||
yield(_INTL("Preparing to generate teams"))
|
||||
rule = rule.copy.setNumber(2)
|
||||
yield(nil)
|
||||
party = load_data(tag + ".rxdata") rescue []
|
||||
teams = load_data(tag + "teams.rxdata") rescue []
|
||||
if teams.length < 10
|
||||
btpokemon = pbGetBTPokemon(tag)
|
||||
if btpokemon && btpokemon.length != 0
|
||||
suggestedLevel = rule.ruleset.suggestedLevel
|
||||
for pk in btpokemon
|
||||
pkmn = pk.createPokemon(suggestedLevel, 31, nil)
|
||||
party.push(pkmn) if rule.ruleset.isPokemonValid?(pkmn)
|
||||
end
|
||||
end
|
||||
end
|
||||
yield(nil)
|
||||
party = pbRemoveDuplicates(party)
|
||||
yield(nil)
|
||||
maxteams = 600
|
||||
cutoffrating = 65
|
||||
toolowrating = 40
|
||||
iterations = 11
|
||||
iterations.times do |iter|
|
||||
save_data(party, tag + ".rxdata")
|
||||
yield(_INTL("Generating teams ({1} of {2})", iter + 1, iterations))
|
||||
i = 0
|
||||
while i < teams.length
|
||||
yield(nil) if i % 10 == 0
|
||||
pbReplenishBattlePokemon(party, rule)
|
||||
if teams[i].rating < cutoffrating && teams[i].totalGames >= 80
|
||||
teams[i] = RuledTeam.new(party, rule)
|
||||
elsif teams[i].length < 2
|
||||
teams[i] = RuledTeam.new(party, rule)
|
||||
elsif i >= maxteams
|
||||
teams[i] = nil
|
||||
teams.compact!
|
||||
elsif teams[i].totalGames >= 250
|
||||
# retire
|
||||
for j in 0...teams[i].length
|
||||
party.push(teams[i][j])
|
||||
end
|
||||
teams[i] = RuledTeam.new(party,rule)
|
||||
elsif teams[i].rating < toolowrating
|
||||
teams[i] = RuledTeam.new(party,rule)
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
save_data(teams, tag + "teams.rxdata")
|
||||
yield(nil)
|
||||
while teams.length < maxteams
|
||||
yield(nil) if teams.length % 10 == 0
|
||||
pbReplenishBattlePokemon(party, rule)
|
||||
teams.push(RuledTeam.new(party, rule))
|
||||
end
|
||||
save_data(party, tag + ".rxdata")
|
||||
teams = teams.sort { |a, b| b.rating <=> a.rating }
|
||||
yield(_INTL("Simulating battles ({1} of {2})", iter + 1, iterations))
|
||||
i = 0
|
||||
loop do
|
||||
changed = false
|
||||
teams.length.times { |j|
|
||||
yield(nil)
|
||||
other = j
|
||||
5.times do
|
||||
other = rand(teams.length)
|
||||
next if other == j
|
||||
end
|
||||
next if other == j
|
||||
changed = true
|
||||
pbRuledBattle(teams[j], teams[other], rule)
|
||||
}
|
||||
i += 1
|
||||
gameCount = 0
|
||||
for team in teams
|
||||
gameCount += team.games
|
||||
end
|
||||
yield(nil)
|
||||
if gameCount / teams.length >= 12
|
||||
for team in teams
|
||||
games = team.games
|
||||
team.updateRating
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
teams.sort! { |a, b| b.rating <=> a.rating }
|
||||
save_data(teams, tag + "teams.rxdata")
|
||||
end
|
||||
party = []
|
||||
yield(nil)
|
||||
teams.sort! { |a, b| a.rating <=> b.rating }
|
||||
for team in teams
|
||||
next if team.rating <= cutoffrating
|
||||
for i in 0...team.length
|
||||
party.push(team[i])
|
||||
end
|
||||
end
|
||||
rule = oldrule
|
||||
yield(nil)
|
||||
party = pbRemoveDuplicates(party)
|
||||
yield(_INTL("Writing results"))
|
||||
party = pbArrangeByTier(party, rule)
|
||||
yield(nil)
|
||||
pbTrainerInfo(party, tag, rule) { yield(nil) }
|
||||
yield(nil)
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
def pbWriteCup(id, rules)
|
||||
return if !$DEBUG
|
||||
trlists = (load_data("Data/trainer_lists.dat") rescue [])
|
||||
list = []
|
||||
for i in 0...trlists.length
|
||||
tr = trlists[i]
|
||||
if tr[5]
|
||||
list.push("*" + (tr[3].sub(/\.txt$/, "")))
|
||||
else
|
||||
list.push((tr[3].sub(/\.txt$/, "")))
|
||||
end
|
||||
end
|
||||
cmd = 0
|
||||
if trlists.length != 0
|
||||
cmd = pbMessage(_INTL("Generate Pokémon teams for this challenge?"),
|
||||
[_INTL("NO"), _INTL("YES, USE EXISTING"), _INTL("YES, USE NEW")], 1)
|
||||
else
|
||||
cmd = pbMessage(_INTL("Generate Pokémon teams for this challenge?"),
|
||||
[_INTL("YES"), _INTL("NO")], 2)
|
||||
if cmd == 0
|
||||
cmd = 2
|
||||
elsif cmd == 1
|
||||
cmd = 0
|
||||
end
|
||||
end
|
||||
return if cmd == 0 # No
|
||||
if cmd == 1 # Yes, use existing
|
||||
cmd = pbMessage(_INTL("Choose a challenge."), list, -1)
|
||||
if cmd >= 0
|
||||
pbMessage(_INTL("This challenge will use the Pokémon list from {1}.", list[cmd]))
|
||||
for i in 0...trlists.length
|
||||
tr = trlists[i]
|
||||
while !tr[5] && tr[2].include?(id)
|
||||
tr[2].delete(id)
|
||||
end
|
||||
end
|
||||
trlists[cmd][2].push(id) if !trlists[cmd][5]
|
||||
save_data(trlists, "Data/trainer_lists.dat")
|
||||
Graphics.update
|
||||
Compiler.write_trainer_lists
|
||||
end
|
||||
return
|
||||
elsif cmd == 2 # Yes, use new
|
||||
return if !pbConfirmMessage(_INTL("This may take a long time. Are you sure?"))
|
||||
mw = pbCreateMessageWindow
|
||||
t = Time.now
|
||||
pbGenerateChallenge(rules, id) { |message|
|
||||
if Time.now - t >= 5
|
||||
Graphics.update
|
||||
t = Time.now
|
||||
end
|
||||
if message
|
||||
pbMessageDisplay(mw, message, false)
|
||||
Graphics.update
|
||||
t = Time.now
|
||||
end
|
||||
}
|
||||
pbDisposeMessageWindow(mw)
|
||||
pbMessage(_INTL("Team generation complete."))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,366 @@
|
||||
$baseStatTotal = {}
|
||||
$babySpecies = {}
|
||||
$minimumLevel = {}
|
||||
$evolutions = {}
|
||||
$legalMoves = {} # For each species, all the moves they have access to
|
||||
$legalMovesLevel = 0 # Level for which $legalMoves were calculated
|
||||
$tmMoves = nil # Array of all moves teachable by a HM/TM/TR
|
||||
|
||||
def pbBaseStatTotal(species)
|
||||
baseStats = GameData::Species.get(species).base_stats
|
||||
ret = 0
|
||||
baseStats.each { |s| ret += s }
|
||||
return ret
|
||||
end
|
||||
|
||||
def baseStatTotal(species)
|
||||
$baseStatTotal[species] = pbBaseStatTotal(species) if !$baseStatTotal[species]
|
||||
return $baseStatTotal[species]
|
||||
end
|
||||
|
||||
def babySpecies(species)
|
||||
$babySpecies[species] = GameData::Species.get(species).get_baby_species if !$babySpecies[species]
|
||||
return $babySpecies[species]
|
||||
end
|
||||
|
||||
def minimumLevel(species)
|
||||
$minimumLevel[species] = GameData::Species.get(species).minimum_level if !$minimumLevel[species]
|
||||
return $minimumLevel[species]
|
||||
end
|
||||
|
||||
def evolutions(species)
|
||||
$evolutions[species] = GameData::Species.get(species).get_evolutions(true) if !$evolutions[species]
|
||||
return $evolutions[species]
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
# Used to replace Sketch with any other move.
|
||||
def pbRandomMove
|
||||
keys = GameData::Move::DATA.keys
|
||||
loop do
|
||||
move_id = keys[rand(keys.length)]
|
||||
move = GameData::Move.get(move_id)
|
||||
next if move.id_number > 384 || move.id == :SKETCH || move.id == :STRUGGLE
|
||||
return move.id
|
||||
end
|
||||
end
|
||||
|
||||
def pbGetLegalMoves2(species, maxlevel)
|
||||
species_data = GameData::Species.get(species)
|
||||
moves = []
|
||||
return moves if !species_data
|
||||
# Populate available moves array (moves)
|
||||
species_data.moves.each { |m| addMove(moves, m[1], 2) if m[0] <= maxlevel }
|
||||
if !$tmMoves
|
||||
$tmMoves = []
|
||||
GameData::Item.each { |i| $tmMoves.push(i.move) if i.is_machine? }
|
||||
end
|
||||
species_data.tutor_moves.each { |m| addMove(moves, m, 0) if $tmMoves.include?(m) }
|
||||
babyspecies = babySpecies(species)
|
||||
GameData::Species.get(babyspecies).egg_moves.each { |m| addMove(moves, m, 2) }
|
||||
#
|
||||
movedatas = []
|
||||
for move in moves
|
||||
movedatas.push([move, GameData::Move.get(move)])
|
||||
end
|
||||
# Delete less powerful moves
|
||||
deleteAll = proc { |a, item|
|
||||
while a.include?(item)
|
||||
a.delete(item)
|
||||
end
|
||||
}
|
||||
for move in moves
|
||||
md = GameData::Move.get(move)
|
||||
for move2 in movedatas
|
||||
# If we have a move that always hits, remove all other moves with no
|
||||
# effect of the same type and <= base power
|
||||
if md.function_code == "0A5" && move2[1].function_code == "000" && # Always hits
|
||||
md.type == move2[1].type && md.base_damage >= move2[1].base_damage
|
||||
deleteAll.call(moves, move2[0])
|
||||
# If we have two status moves that have the same function code, delete the
|
||||
# one with lower accuracy (Supersonic vs. Confuse Ray, etc.)
|
||||
elsif md.function_code == move2[1].function_code && md.base_damage == 0 &&
|
||||
move2[1].base_damage == 0 && md.accuracy > move2[1].accuracy
|
||||
deleteAll.call(moves, move2[0])
|
||||
# Delete poison-causing moves if we have a move that causes toxic
|
||||
elsif md.function_code == "006" && move2[1].function_code == "005"
|
||||
deleteAll.call(moves, move2[0])
|
||||
# If we have two moves with the same function code and type, and one of
|
||||
# them is damaging and has 10/15/the same PP as the other move and EITHER
|
||||
# does more damage than the other move OR does the same damage but is more
|
||||
# accurate, delete the other move (Surf, Flamethrower, Thunderbolt, etc.)
|
||||
elsif md.function_code == move2[1].function_code && md.base_damage != 0 &&
|
||||
md.type == move2[1].type &&
|
||||
(md.total_pp == 15 || md.total_pp == 10 || md.total_pp == move2[1].total_pp) &&
|
||||
(md.base_damage > move2[1].base_damage ||
|
||||
(md.base_damage == move2[1].base_damage && md.accuracy > move2[1].accuracy))
|
||||
deleteAll.call(moves, move2[0])
|
||||
end
|
||||
end
|
||||
end
|
||||
return moves
|
||||
end
|
||||
|
||||
def addMove(moves, move, base)
|
||||
data = GameData::Move.get(move)
|
||||
return if moves.include?(data.id)
|
||||
return if [:BUBBLE, :BUBBLEBEAM].include?(data.id) # Never add these moves
|
||||
count = base + 1 # Number of times to add move to moves
|
||||
count = base if data.function_code == "000" && data.base_damage <= 40
|
||||
if data.base_damage <= 30 || [:GROWL, :TAILWHIP, :LEER].include?(data.id)
|
||||
count = base
|
||||
end
|
||||
if data.base_damage >= 60 ||
|
||||
[:REFLECT, :LIGHTSCREEN, :SAFEGUARD, :SUBSTITUTE, :FAKEOUT].include?(data.id)
|
||||
count = base + 2
|
||||
end
|
||||
if data.base_damage >= 80 && data.type == :NORMAL
|
||||
count = base + 3
|
||||
end
|
||||
if [:PROTECT, :DETECT, :TOXIC, :AERIALACE, :WILLOWISP, :SPORE, :THUNDERWAVE,
|
||||
:HYPNOSIS, :CONFUSERAY, :ENDURE, :SWORDSDANCE].include?(data.id)
|
||||
count = base + 3
|
||||
end
|
||||
count.times { moves.push(data.id) }
|
||||
end
|
||||
|
||||
# Returns whether moves contains any move with the same type as thismove but
|
||||
# with a higher base damage than it.
|
||||
def hasMorePowerfulMove(moves, thismove)
|
||||
thisdata = GameData::Move.get(thismove)
|
||||
return false if thisdata.base_damage == 0
|
||||
for move in moves
|
||||
next if !move
|
||||
moveData = GameData::Move.get(move)
|
||||
if moveData.type == thisdata.type && moveData.base_damage > thisdata.base_damage
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Generate a random Pokémon that adheres to the given rules.
|
||||
#===============================================================================
|
||||
def pbRandomPokemonFromRule(rules, trainer)
|
||||
pkmn = nil
|
||||
iteration = -1
|
||||
loop do
|
||||
iteration += 1
|
||||
species = nil
|
||||
level = rules.ruleset.suggestedLevel
|
||||
keys = GameData::Species::DATA.keys
|
||||
loop do
|
||||
loop do
|
||||
species = keys[rand(keys.length)]
|
||||
break if GameData::Species.get(species).form == 0
|
||||
end
|
||||
r = rand(20)
|
||||
bst = baseStatTotal(species)
|
||||
next if level < minimumLevel(species)
|
||||
if iteration % 2 == 0
|
||||
next if r < 16 && bst < 400
|
||||
next if r < 13 && bst < 500
|
||||
else
|
||||
next if bst > 400
|
||||
next if r < 10 && babySpecies(species) != species
|
||||
end
|
||||
next if r < 10 && babySpecies(species) == species
|
||||
next if r < 7 && evolutions(species).length > 0
|
||||
break
|
||||
end
|
||||
ev = []
|
||||
GameData::Stat.each_main { |s| ev.push(s.id) if rand(100) < 50 }
|
||||
nature = nil
|
||||
keys = GameData::Nature::DATA.keys
|
||||
loop do
|
||||
nature = keys[rand(keys.length)]
|
||||
nature_data = GameData::Nature.get(nature)
|
||||
if [:LAX, :GENTLE].include?(nature_data.id) || nature_data.stat_changes.length == 0
|
||||
next if rand(20) < 19
|
||||
else
|
||||
raised_emphasis = false
|
||||
lowered_emphasis = false
|
||||
nature_data.stat_changes.each do |change|
|
||||
next if !ev.include?(change[0])
|
||||
raised_emphasis = true if change[1] > 0
|
||||
lowered_emphasis = true if change[1] < 0
|
||||
end
|
||||
next if rand(10) < 6 && !raised_emphasis
|
||||
next if rand(10) < 9 && lowered_emphasis
|
||||
end
|
||||
break
|
||||
end
|
||||
$legalMoves = {} if level != $legalMovesLevel
|
||||
$legalMovesLevel = level
|
||||
$legalMoves[species] = pbGetLegalMoves2(species, level) if !$legalMoves[species]
|
||||
itemlist = [
|
||||
:ORANBERRY, :SITRUSBERRY, :ADAMANTORB, :BABIRIBERRY,
|
||||
:BLACKSLUDGE, :BRIGHTPOWDER, :CHESTOBERRY, :CHOICEBAND,
|
||||
:CHOICESCARF, :CHOICESPECS, :CHOPLEBERRY, :DAMPROCK,
|
||||
:DEEPSEATOOTH, :EXPERTBELT, :FLAMEORB, :FOCUSSASH,
|
||||
:FOCUSBAND, :HEATROCK, :LEFTOVERS, :LIFEORB, :LIGHTBALL,
|
||||
:LIGHTCLAY, :LUMBERRY, :OCCABERRY, :PETAYABERRY, :SALACBERRY,
|
||||
:SCOPELENS, :SHEDSHELL, :SHELLBELL, :SHUCABERRY, :LIECHIBERRY,
|
||||
:SILKSCARF, :THICKCLUB, :TOXICORB, :WIDELENS, :YACHEBERRY,
|
||||
:HABANBERRY, :SOULDEW, :PASSHOBERRY, :QUICKCLAW, :WHITEHERB
|
||||
]
|
||||
# Most used: Leftovers, Life Orb, Choice Band, Choice Scarf, Focus Sash
|
||||
item = nil
|
||||
loop do
|
||||
if rand(40) == 0
|
||||
item = :LEFTOVERS
|
||||
break
|
||||
end
|
||||
item = itemlist[rand(itemlist.length)]
|
||||
next if !item
|
||||
case item
|
||||
when :LIGHTBALL
|
||||
next if species != :PIKACHU
|
||||
when :SHEDSHELL
|
||||
next if species != :FORRETRESS && species != :SKARMORY
|
||||
when :SOULDEW
|
||||
next if species != :LATIOS && species != :LATIAS
|
||||
when :FOCUSSASH
|
||||
next if baseStatTotal(species) > 450 && rand(10) < 8
|
||||
when :ADAMANTORB
|
||||
next if species != :DIALGA
|
||||
when :PASSHOBERRY
|
||||
next if species != :STEELIX
|
||||
when :BABIRIBERRY
|
||||
next if species != :TYRANITAR
|
||||
when :HABANBERRY
|
||||
next if species != :GARCHOMP
|
||||
when :OCCABERRY
|
||||
next if species != :METAGROSS
|
||||
when :CHOPLEBERRY
|
||||
next if species != :UMBREON
|
||||
when :YACHEBERRY
|
||||
next if ![:TORTERRA, :GLISCOR, :DRAGONAIR].include?(species)
|
||||
when :SHUCABERRY
|
||||
next if species != :HEATRAN
|
||||
when :DEEPSEATOOTH
|
||||
next if species != :CLAMPERL
|
||||
when :THICKCLUB
|
||||
next if ![:CUBONE, :MAROWAK].include?(species)
|
||||
when :LIECHIBERRY
|
||||
ev.push(:ATTACK) if !ev.include?(:ATTACK) && rand(100) < 50
|
||||
when :SALACBERRY
|
||||
ev.push(:SPEED) if !ev.include?(:SPEED) && rand(100) < 50
|
||||
when :PETAYABERRY
|
||||
ev.push(:SPECIAL_ATTACK) if !ev.include?(:SPECIAL_ATTACK) && rand(100) < 50
|
||||
end
|
||||
break
|
||||
end
|
||||
if level < 10 && GameData::Item.exists?(:ORANBERRY)
|
||||
item = :ORANBERRY if rand(40) == 0 || item == :SITRUSBERRY
|
||||
elsif level > 20 && GameData::Item.exists?(:SITRUSBERRY)
|
||||
item = :SITRUSBERRY if rand(40) == 0 || item == :ORANBERRY
|
||||
end
|
||||
moves = $legalMoves[species]
|
||||
sketch = false
|
||||
if moves[0] == :SKETCH
|
||||
sketch = true
|
||||
for m in 0...Pokemon::MAX_MOVES
|
||||
moves[m] = pbRandomMove
|
||||
end
|
||||
end
|
||||
next if moves.length == 0
|
||||
if (moves | []).length < Pokemon::MAX_MOVES
|
||||
moves = [:TACKLE] if moves.length == 0
|
||||
moves |= []
|
||||
else
|
||||
newmoves = []
|
||||
rest = GameData::Move.exists?(:REST) ? :REST : nil
|
||||
spitup = GameData::Move.exists?(:SPITUP) ? :SPITUP : nil
|
||||
swallow = GameData::Move.exists?(:SWALLOW) ? :SWALLOW : nil
|
||||
stockpile = GameData::Move.exists?(:STOCKPILE) ? :STOCKPILE : nil
|
||||
snore = GameData::Move.exists?(:SNORE) ? :SNORE : nil
|
||||
sleeptalk = GameData::Move.exists?(:SLEEPTALK) ? :SLEEPTALK : nil
|
||||
loop do
|
||||
newmoves.clear
|
||||
while newmoves.length < [moves.length, Pokemon::MAX_MOVES].min
|
||||
m = moves[rand(moves.length)]
|
||||
next if rand(100) < 50 && hasMorePowerfulMove(moves, m)
|
||||
newmoves.push(m) if m && !newmoves.include?(m)
|
||||
end
|
||||
if (newmoves.include?(spitup) || newmoves.include?(swallow)) &&
|
||||
!newmoves.include?(stockpile)
|
||||
next unless sketch
|
||||
end
|
||||
if (!newmoves.include?(spitup) && !newmoves.include?(swallow)) &&
|
||||
newmoves.include?(stockpile)
|
||||
next unless sketch
|
||||
end
|
||||
if newmoves.include?(sleeptalk) && !newmoves.include?(rest)
|
||||
next unless (sketch || !moves.include?(rest)) && rand(100) < 20
|
||||
end
|
||||
if newmoves.include?(snore) && !newmoves.include?(rest)
|
||||
next unless (sketch || !moves.include?(rest)) && rand(100) < 20
|
||||
end
|
||||
totalbasedamage = 0
|
||||
hasPhysical = false
|
||||
hasSpecial = false
|
||||
hasNormal = false
|
||||
for move in newmoves
|
||||
d = GameData::Move.get(move)
|
||||
if d.base_damage >= 1
|
||||
totalbasedamage += d.base_damage
|
||||
hasNormal = true if d.type == :NORMAL
|
||||
hasPhysical = true if d.category == 0
|
||||
hasSpecial = true if d.category == 1
|
||||
end
|
||||
end
|
||||
if !hasPhysical && ev.include?(:ATTACK)
|
||||
# No physical attack, but emphasizes Attack
|
||||
next if rand(100) < 80
|
||||
end
|
||||
if !hasSpecial && ev.include?(:SPECIAL_ATTACK)
|
||||
# No special attack, but emphasizes Special Attack
|
||||
next if rand(100) < 80
|
||||
end
|
||||
r = rand(10)
|
||||
next if r > 6 && totalbasedamage > 180
|
||||
next if r > 8 && totalbasedamage > 140
|
||||
next if totalbasedamage == 0 && rand(100) < 95
|
||||
############
|
||||
# Moves accepted
|
||||
if hasPhysical && !hasSpecial
|
||||
ev.push(:ATTACK) if rand(100) < 80
|
||||
ev.delete(:SPECIAL_ATTACK) if rand(100) < 80
|
||||
end
|
||||
if !hasPhysical && hasSpecial
|
||||
ev.delete(:ATTACK) if rand(100) < 80
|
||||
ev.push(:SPECIAL_ATTACK) if rand(100) < 80
|
||||
end
|
||||
item = :LEFTOVERS if !hasNormal && item == :SILKSCARF
|
||||
moves = newmoves
|
||||
break
|
||||
end
|
||||
end
|
||||
if item == :LIGHTCLAY && !moves.any? { |m| m == :LIGHTSCREEN || m = :REFLECT }
|
||||
item = :LEFTOVERS
|
||||
end
|
||||
if item == :BLACKSLUDGE
|
||||
type1 = GameData::Species.get(species).type1
|
||||
type2 = GameData::Species.get(species).type2 || type1
|
||||
item = :LEFTOVERS if type1 != :POISON && type2 != :POISON
|
||||
end
|
||||
if item == :HEATROCK && !moves.any? { |m| m == :SUNNYDAY }
|
||||
item = :LEFTOVERS
|
||||
end
|
||||
if item == :DAMPROCK && !moves.any? { |m| m == :RAINDANCE }
|
||||
item = :LEFTOVERS
|
||||
end
|
||||
if moves.any? { |m| m == :REST }
|
||||
item = :LUMBERRY if rand(100) < 33
|
||||
item = :CHESTOBERRY if rand(100) < 25
|
||||
end
|
||||
pk = PBPokemon.new(species, item, nature, moves[0], moves[1], moves[2], moves[3], ev)
|
||||
pkmn = pk.createPokemon(level, 31, trainer)
|
||||
break if rules.ruleset.isPokemonValid?(pkmn)
|
||||
end
|
||||
return pkmn
|
||||
end
|
||||
@@ -0,0 +1,215 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
def getTypes(species)
|
||||
species_data = GameData::Species.get(species)
|
||||
type1 = species_data.type1
|
||||
type2 = species_data.type2
|
||||
return (type1 == type2) ? [type1] : [type1, type2]
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# If no trainers are defined for the current challenge, generate a set of random
|
||||
# ones for it. If pokemonlist is given, assign Pokémon from it to all trainers.
|
||||
# Save the results in the appropriate PBS files.
|
||||
#===============================================================================
|
||||
def pbTrainerInfo(pokemonlist, trfile, rules)
|
||||
bttrainers = pbGetBTTrainers(trfile)
|
||||
btpokemon = pbGetBTPokemon(trfile)
|
||||
# No battle trainers found; fill bttrainers with 200 randomly chosen ones from
|
||||
# all that exist (with a base money < 100)
|
||||
if bttrainers.length == 0
|
||||
for i in 0...200
|
||||
yield(nil) if block_given? && i % 50 == 0
|
||||
trainerid = nil
|
||||
if GameData::TrainerType.exists?(:YOUNGSTER) && rand(30) == 0
|
||||
trainerid = :YOUNGSTER
|
||||
else
|
||||
tr_typekeys = GameData::TrainerType::DATA.keys
|
||||
loop do
|
||||
tr_type = tr_typekeys[rand(tr_typekeys.length)]
|
||||
tr_type_data = GameData::TrainerType.get(tr_type)
|
||||
next if tr_type_data.base_money >= 100
|
||||
trainerid = tr_type_data.id
|
||||
end
|
||||
end
|
||||
# Create a random name for the trainer
|
||||
gender = GameData::TrainerType.get(trainerid).gender
|
||||
randomName = getRandomNameEx(gender, nil, 0, 12)
|
||||
# Add the trainer to bttrainers
|
||||
tr = [trainerid, randomName, _INTL("Here I come!"), _INTL("Yes, I won!"),
|
||||
_INTL("Man, I lost!"), []]
|
||||
bttrainers.push(tr)
|
||||
end
|
||||
# Sort all the randomly chosen trainers by their base money (smallest first)
|
||||
bttrainers.sort! { |a, b|
|
||||
money1 = GameData::TrainerType.get(a[0]).base_money
|
||||
money2 = GameData::TrainerType.get(b[0]).base_money
|
||||
next (money1 == money2) ? a[0].to_s <=> b[0].to_s : money1 <=> money2
|
||||
}
|
||||
end
|
||||
yield(nil) if block_given?
|
||||
# Set all Pokémon in pokemonlist to the appropriate level, and determine their
|
||||
# type(s) and whether they are valid for the given rules
|
||||
suggestedLevel = rules.ruleset.suggestedLevel
|
||||
rulesetTeam = rules.ruleset.copy.clearPokemonRules
|
||||
pkmntypes = []
|
||||
validities = []
|
||||
for pkmn in pokemonlist
|
||||
pkmn.level = suggestedLevel if pkmn.level != suggestedLevel
|
||||
pkmntypes.push(getTypes(pkmn.species))
|
||||
validities.push(rules.ruleset.isPokemonValid?(pkmn))
|
||||
end
|
||||
# For each trainer in bttrainers, come up with a set of Pokémon taken from
|
||||
# pokemonlist for that trainer, and copy the trainer and their set of Pokémon
|
||||
# to newbttrainers
|
||||
newbttrainers = []
|
||||
for btt in 0...bttrainers.length
|
||||
yield(nil) if block_given? && btt % 50 == 0
|
||||
trainerdata = bttrainers[btt]
|
||||
pokemonnumbers = trainerdata[5] || []
|
||||
# Find all the Pokémon available to the trainer, and count up how often
|
||||
# those Pokémon have each type
|
||||
species = []
|
||||
types = {}
|
||||
GameData::Type.each { |t| types[t.id] = 0 }
|
||||
for pn in pokemonnumbers
|
||||
pkmn = btpokemon[pn]
|
||||
species.push(pkmn.species)
|
||||
t = getTypes(pkmn.species)
|
||||
t.each { |typ| types[typ] += 1 }
|
||||
end
|
||||
species |= [] # remove duplicates
|
||||
# Scale down the counts of each type to the range 0 -> 10
|
||||
count = 0
|
||||
GameData::Type.each do |t|
|
||||
if types[t.id] >= 5
|
||||
types[t.id] /= 4
|
||||
types[t.id] = 10 if types[t.id] > 10
|
||||
else
|
||||
types[t.id] = 0
|
||||
end
|
||||
count += types[t.id]
|
||||
end
|
||||
types[:NORMAL] = 1 if count == 0 # All type counts are 0; add 1 to Normal
|
||||
# Trainer had no Pokémon available to it; make all the type counts 1
|
||||
if pokemonnumbers.length == 0
|
||||
GameData::Type.each { |t| types[t.id] = 1 }
|
||||
end
|
||||
# Get Pokémon from pokemonlist, if there are any, and make sure enough are
|
||||
# gotten that a valid team can be made from them
|
||||
numbers = []
|
||||
if pokemonlist
|
||||
# For each valid Pokémon in pokemonlist, add its index within pokemonlist
|
||||
# to numbers, but only if that species is available to the trainer.
|
||||
# Pokémon are less likely to be added if it is positioned later in
|
||||
# pokemonlist, or if the trainer is positioned earlier in bttrainers (i.e.
|
||||
# later trainers get better Pokémon).
|
||||
numbersPokemon = []
|
||||
for index in 0...pokemonlist.length
|
||||
next if !validities[index]
|
||||
pkmn = pokemonlist[index]
|
||||
absDiff = ((index * 8 / pokemonlist.length) - (btt * 8 / bttrainers.length)).abs
|
||||
if species.include?(pkmn.species)
|
||||
weight = [32, 12, 5, 2, 1, 0, 0, 0][[absDiff, 7].min]
|
||||
if rand(40) < weight
|
||||
numbers.push(index)
|
||||
numbersPokemon.push(pokemonlist[index])
|
||||
end
|
||||
else
|
||||
# Pokémon's species isn't available to the trainer; try adding it
|
||||
# anyway (more likely to add it if the trainer has access to more
|
||||
# Pokémon of the same type(s) as this Pokémon)
|
||||
t = pkmntypes[index]
|
||||
t.each { |typ|
|
||||
weight = [32, 12, 5, 2, 1, 0, 0, 0][[absDiff, 7].min]
|
||||
weight *= types[typ]
|
||||
if rand(40) < weight
|
||||
numbers.push(index)
|
||||
numbersPokemon.push(pokemonlist[index])
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
numbers |= [] # Remove duplicates
|
||||
# If there aren't enough Pokémon to form a full team, or a valid team
|
||||
# can't be made from them, fill up numbers with Pokémon in pokemonlist
|
||||
# that EITHER have the same species as one available to the trainer OR has
|
||||
# a type that is available to the trainer, until a valid team can be
|
||||
# formed from what's in numbers
|
||||
if numbers.length < Settings::MAX_PARTY_SIZE ||
|
||||
!rulesetTeam.hasValidTeam?(numbersPokemon)
|
||||
for index in 0...pokemonlist.length
|
||||
pkmn = pokemonlist[index]
|
||||
next if !validities[index]
|
||||
if species.include?(pkmn.species)
|
||||
numbers.push(index)
|
||||
numbersPokemon.push(pokemonlist[index])
|
||||
else
|
||||
t = pkmntypes[index]
|
||||
t.each { |typ|
|
||||
if types[typ] > 0 && !numbers.include?(index)
|
||||
numbers.push(index)
|
||||
numbersPokemon.push(pokemonlist[index])
|
||||
break
|
||||
end
|
||||
}
|
||||
end
|
||||
break if numbers.length >= Settings::MAX_PARTY_SIZE && rules.ruleset.hasValidTeam?(numbersPokemon)
|
||||
end
|
||||
# If there STILL aren't enough Pokémon to form a full team, or a valid
|
||||
# team can't be made from them, add random Pokémon from pokemonlist
|
||||
# until a valid team can be formed from what's in numbers
|
||||
if numbers.length < Settings::MAX_PARTY_SIZE || !rules.ruleset.hasValidTeam?(numbersPokemon)
|
||||
while numbers.length < pokemonlist.length &&
|
||||
(numbers.length < Settings::MAX_PARTY_SIZE || !rules.ruleset.hasValidTeam?(numbersPokemon))
|
||||
index = rand(pokemonlist.length)
|
||||
if !numbers.include?(index)
|
||||
numbers.push(index)
|
||||
numbersPokemon.push(pokemonlist[index])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
numbers.sort!
|
||||
end
|
||||
# Add the trainer's data, including all Pokémon that should be available to
|
||||
# it (from pokemonlist), to newbttrainers
|
||||
newbttrainers.push([trainerdata[0], trainerdata[1], trainerdata[2],
|
||||
trainerdata[3], trainerdata[4], numbers])
|
||||
end
|
||||
yield(nil) if block_given?
|
||||
# Add the trainer and Pokémon data from above to trainer_lists.dat, and then
|
||||
# create all PBS files from it
|
||||
pbpokemonlist = []
|
||||
for pkmn in pokemonlist
|
||||
pbpokemonlist.push(PBPokemon.fromPokemon(pkmn))
|
||||
end
|
||||
trlists = (load_data("Data/trainer_lists.dat") rescue [])
|
||||
hasDefault = false
|
||||
trIndex = -1
|
||||
for i in 0...trlists.length
|
||||
next if !trlists[i][5]
|
||||
hasDefault = true
|
||||
break
|
||||
end
|
||||
for i in 0...trlists.length
|
||||
if trlists[i][2].include?(trfile)
|
||||
trIndex = i
|
||||
trlists[i][0] = newbttrainers
|
||||
trlists[i][1] = pbpokemonlist
|
||||
trlists[i][5] = !hasDefault
|
||||
end
|
||||
end
|
||||
yield(nil) if block_given?
|
||||
if trIndex < 0
|
||||
info = [newbttrainers, pbpokemonlist, [trfile],
|
||||
trfile + "tr.txt", trfile + "pm.txt", !hasDefault]
|
||||
trlists.push(info)
|
||||
end
|
||||
yield(nil) if block_given?
|
||||
save_data(trlists, "Data/trainer_lists.dat")
|
||||
yield(nil) if block_given?
|
||||
Compiler.write_trainer_lists
|
||||
yield(nil) if block_given?
|
||||
end
|
||||
@@ -0,0 +1,433 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class RuledTeam
|
||||
attr_accessor :team
|
||||
|
||||
def initialize(party, rule)
|
||||
count = rule.ruleset.suggestedNumber
|
||||
@team = []
|
||||
retnum = []
|
||||
loop do
|
||||
for i in 0...count
|
||||
retnum[i] = rand(party.length)
|
||||
@team[i] = party[retnum[i]]
|
||||
party.delete_at(retnum[i])
|
||||
end
|
||||
break if rule.ruleset.isValid?(@team)
|
||||
end
|
||||
@totalGames = 0
|
||||
@rating = PlayerRating.new
|
||||
@history = MatchHistory.new(@rating)
|
||||
end
|
||||
|
||||
def [](i)
|
||||
@team[i]
|
||||
end
|
||||
|
||||
def length
|
||||
return @team.length
|
||||
end
|
||||
|
||||
def rating
|
||||
@rating.winChancePercent
|
||||
end
|
||||
|
||||
def ratingData
|
||||
@rating
|
||||
end
|
||||
|
||||
def ratingRaw
|
||||
[@rating.rating, @rating.deviation, @rating.volatility, @rating.winChancePercent]
|
||||
end
|
||||
|
||||
def compare(other)
|
||||
@rating.compare(other.ratingData)
|
||||
end
|
||||
|
||||
def totalGames
|
||||
(@totalGames || 0) + self.games
|
||||
end
|
||||
|
||||
def addMatch(other,score)
|
||||
@history.addMatch(other.ratingData, score)
|
||||
end
|
||||
|
||||
def games
|
||||
@history.length
|
||||
end
|
||||
|
||||
def updateRating
|
||||
@totalGames = 0 if !@totalGames
|
||||
oldgames = self.games
|
||||
@history.updateAndClear()
|
||||
newgames = self.games
|
||||
@totalGames += (oldgames - newgames)
|
||||
end
|
||||
|
||||
def toStr
|
||||
return "[" + @rating.to_i.to_s + "," + @games.to_i.to_s + "]"
|
||||
end
|
||||
|
||||
def load(party)
|
||||
ret = []
|
||||
for i in 0...team.length
|
||||
ret.push(party[team[i]])
|
||||
end
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class SingleMatch
|
||||
attr_reader :opponentRating
|
||||
attr_reader :opponentDeviation
|
||||
attr_reader :score
|
||||
attr_reader :kValue
|
||||
|
||||
def initialize(opponentRating, opponentDev, score, kValue = 16)
|
||||
@opponentRating = opponentRating
|
||||
@opponentDeviation = opponentDev
|
||||
@score = score # -1=draw, 0=lose, 1=win
|
||||
@kValue = kValue
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class MatchHistory
|
||||
include Enumerable
|
||||
|
||||
def initialize(thisPlayer)
|
||||
@matches = []
|
||||
@thisPlayer = thisPlayer
|
||||
end
|
||||
|
||||
def [](i)
|
||||
@matches[i]
|
||||
end
|
||||
|
||||
def length
|
||||
@matches.length
|
||||
end
|
||||
|
||||
def each
|
||||
@matches.each { |item| yield item }
|
||||
end
|
||||
|
||||
def addMatch(otherPlayer, result)
|
||||
# 1=I won; 0=Other player won; -1: Draw
|
||||
@matches.push(SingleMatch.new(otherPlayer.rating, otherPlayer.deviation, result))
|
||||
end
|
||||
|
||||
def updateAndClear
|
||||
@thisPlayer.update(@matches)
|
||||
@matches.clear
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class PlayerRatingElo
|
||||
attr_reader :rating
|
||||
K_VALUE = 16
|
||||
|
||||
def initialize
|
||||
@rating = 1600.0
|
||||
@deviation = 0
|
||||
@volatility = 0
|
||||
@estimatedRating = nil
|
||||
end
|
||||
|
||||
def winChancePercent
|
||||
return @estimatedRating if @estimatedRating
|
||||
x = (1 + 10.0**((@rating - 1600.0) / 400.0))
|
||||
@estimatedRating = (x == 0 ? 1.0 : 1.0 / x)
|
||||
return @estimatedRating
|
||||
end
|
||||
|
||||
def update(matches)
|
||||
return if matches.length == 0
|
||||
stake = 0
|
||||
matches.length.times do
|
||||
score = (match.score == -1) ? 0.5 : match.score
|
||||
e = (1 + 10.0**((@rating - match.opponentRating) / 400.0))
|
||||
stake += match.kValue * (score - e)
|
||||
end
|
||||
@rating += stake
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class PlayerRating
|
||||
attr_reader :volatility
|
||||
attr_reader :deviation
|
||||
attr_reader :rating
|
||||
|
||||
def initialize
|
||||
@rating = 1500.0
|
||||
@deviation = 350.0
|
||||
@volatility = 0.9
|
||||
@estimatedRating = nil
|
||||
end
|
||||
|
||||
def winChancePercent
|
||||
return @estimatedRating if @estimatedRating
|
||||
if @deviation > 100
|
||||
# https://www.smogon.com/forums/threads/make-sense-of-your-shoddy-battle-rating.55764/
|
||||
otherRating = 1500.0
|
||||
otherDeviation = 350.0
|
||||
s = Math.sqrt(100000.0 + @deviation * @deviation + otherDeviation * otherDeviation)
|
||||
g = 10.0**((otherRating - @rating) * 0.79 / s)
|
||||
@estimatedRating = (1.0 / (1.0 + g)) * 100.0 # Percent chance that I win against opponent
|
||||
else
|
||||
# GLIXARE method
|
||||
rds = @deviation * @deviation
|
||||
sqr = Math.sqrt(15.905694331435 * (rds + 221781.21786254))
|
||||
inner = (1500.0 - @rating) * Math::PI / sqr
|
||||
@estimatedRating = (10000.0 / (1.0 + (10.0**inner)) + 0.5) / 100.0
|
||||
end
|
||||
return @estimatedRating
|
||||
end
|
||||
|
||||
def update(matches, system = 1.2)
|
||||
volatility = volatility2
|
||||
deviation = deviation2
|
||||
rating = rating2
|
||||
if matches.length == 0
|
||||
setDeviation2(Math.sqrt(deviation * deviation + volatility * volatility))
|
||||
return
|
||||
end
|
||||
g = []
|
||||
e = []
|
||||
score = []
|
||||
for i in 0...matches.length
|
||||
match = matches[i]
|
||||
g[i] = getGFactor(match.opponentDeviation)
|
||||
e[i] = getEFactor(rating, match.opponentRating, g[i])
|
||||
score[i] = match.score
|
||||
end
|
||||
# Estimated variance
|
||||
variance = 0.0
|
||||
for i in 0...matches.length
|
||||
variance += g[i] * g[i] * e[i] * (1 - e[i])
|
||||
end
|
||||
variance = 1.0 / variance
|
||||
# Improvement sum
|
||||
sum = 0.0
|
||||
for i in 0...matches.length
|
||||
v = score[i]
|
||||
sum += g[i] * (v.to_f - e[i]) if v != -1
|
||||
end
|
||||
volatility = getUpdatedVolatility(volatility, deviation, variance, sum, system)
|
||||
# Update deviation
|
||||
t = deviation * deviation + volatility * volatility
|
||||
deviation = 1.0 / Math.sqrt(1.0 / t + 1.0 / variance)
|
||||
# Update rating
|
||||
rating = rating + deviation * deviation * sum
|
||||
setRating2(rating)
|
||||
setDeviation2(deviation)
|
||||
setVolatility2(volatility)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_writer :volatility
|
||||
|
||||
alias volatility2 volatility
|
||||
|
||||
def rating2
|
||||
return (@rating - 1500.0) / 173.7178
|
||||
end
|
||||
|
||||
def deviation2
|
||||
return @deviation / 173.7178
|
||||
end
|
||||
|
||||
def getGFactor(deviation)
|
||||
# deviation is not yet in glicko2
|
||||
deviation /= 173.7178
|
||||
return 1.0 / Math.sqrt(1.0 + (3.0 * deviation * deviation) / (Math::PI * Math::PI))
|
||||
end
|
||||
|
||||
def getEFactor(rating, opponentRating, g)
|
||||
# rating is already in glicko2
|
||||
# opponentRating is not yet in glicko2
|
||||
opponentRating = (opponentRating - 1500.0) / 173.7178
|
||||
return 1.0 / (1.0 + Math.exp(-g * (rating - opponentRating)))
|
||||
end
|
||||
|
||||
def setVolatility2(value)
|
||||
@volatility = value
|
||||
end
|
||||
|
||||
def setRating2(value)
|
||||
@estimatedRating = nil
|
||||
@rating = (value * 173.7178) + 1500.0
|
||||
end
|
||||
|
||||
def setDeviation2(value)
|
||||
@estimatedRating = nil
|
||||
@deviation = value * 173.7178
|
||||
end
|
||||
|
||||
def getUpdatedVolatility(volatility, deviation, variance, improvementSum, system)
|
||||
improvement = improvementSum * variance
|
||||
a = Math.log(volatility * volatility)
|
||||
squSystem = system * system
|
||||
squDeviation = deviation * deviation
|
||||
squVariance = variance + variance
|
||||
squDevplusVar = squDeviation + variance
|
||||
x0 = a
|
||||
100.times { # Up to 100 iterations to avoid potentially infinite loops
|
||||
e = Math.exp(x0)
|
||||
d = squDevplusVar + e
|
||||
squD = d * d
|
||||
i = improvement / d
|
||||
h1 = -(x0 - a) / squSystem - 0.5 * e * i * i
|
||||
h2 = -1.0 / squSystem - 0.5 * e * squDevplusVar / squD
|
||||
h2 += 0.5 * squVariance * e * (squDevplusVar - e) / (squD * d)
|
||||
x1 = x0
|
||||
x0 -= h1 / h2
|
||||
break if ((x1 - x0).abs < 0.000001)
|
||||
}
|
||||
return Math.exp(x0 / 2.0)
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
def pbDecideWinnerEffectiveness(move, otype1, otype2, ability, scores)
|
||||
data = GameData::Move.get(move)
|
||||
return 0 if data.base_damage == 0
|
||||
atype = data.type
|
||||
typemod = Effectiveness::NORMAL_EFFECTIVE_ONE ** 2
|
||||
if ability != :LEVITATE || data.type != :GROUND
|
||||
mod1 = Effectiveness.calculate_one(atype, otype1)
|
||||
mod2 = (otype1 == otype2) ? Effectiveness::NORMAL_EFFECTIVE_ONE : Effectiveness.calculate_one(atype, otype2)
|
||||
if ability == :WONDERGUARD
|
||||
mod1 = Effectiveness::NORMAL_EFFECTIVE_ONE if mod1 <= Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
mod2 = Effectiveness::NORMAL_EFFECTIVE_ONE if mod2 <= Effectiveness::NORMAL_EFFECTIVE_ONE
|
||||
end
|
||||
typemod = mod1 * mod2
|
||||
end
|
||||
return scores[0] if typemod == 0 # Ineffective
|
||||
return scores[1] if typemod == 1 # Doubly not very effective
|
||||
return scores[2] if typemod == 2 # Not very effective
|
||||
return scores[3] if typemod == 4 # Normal effective
|
||||
return scores[4] if typemod == 8 # Super effective
|
||||
return scores[5] if typemod == 16 # Doubly super effective
|
||||
return 0
|
||||
end
|
||||
|
||||
def pbDecideWinnerScore(party0, party1, rating)
|
||||
score = 0
|
||||
types1 = []
|
||||
types2 = []
|
||||
abilities = []
|
||||
for j in 0...party1.length
|
||||
types1.push(party1[j].type1)
|
||||
types2.push(party1[j].type2)
|
||||
abilities.push(party1[j].ability_id)
|
||||
end
|
||||
for i in 0...party0.length
|
||||
for move in party0[i].moves
|
||||
next if !move
|
||||
for j in 0...party1.length
|
||||
score += pbDecideWinnerEffectiveness(move.id,
|
||||
types1[j], types2[j], abilities[j], [-16, -8, 0, 4, 12, 20])
|
||||
end
|
||||
end
|
||||
basestatsum = baseStatTotal(party0[i].species)
|
||||
score += basestatsum / 10
|
||||
score += 10 if party0[i].item # Not in Battle Dome ranking
|
||||
end
|
||||
score += rating + rand(32)
|
||||
return score
|
||||
end
|
||||
|
||||
def pbDecideWinner(party0, party1, rating0, rating1)
|
||||
rating0 = (rating0 * 15.0 / 100).round
|
||||
rating1 = (rating1 * 15.0 / 100).round
|
||||
score0 = pbDecideWinnerScore(party0, party1, rating0)
|
||||
score1 = pbDecideWinnerScore(party1, party0, rating1)
|
||||
if score0 == score1
|
||||
return 5 if rating0 == rating1
|
||||
return (rating0 > rating1) ? 1 : 2
|
||||
else
|
||||
return (score0 > score1) ? 1 : 2
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
def pbRuledBattle(team1, team2, rule)
|
||||
decision = 0
|
||||
if rand(100) != 0
|
||||
party1 = []
|
||||
party2 = []
|
||||
team1.length.times { |i| party1.push(team1[i]) }
|
||||
team2.length.times { |i| party2.push(team2[i]) }
|
||||
decision = pbDecideWinner(party1, party2, team1.rating, team2.rating)
|
||||
else
|
||||
level = rule.ruleset.suggestedLevel
|
||||
t_type = nil
|
||||
GameData::TrainerType.each { |t| t_type = t.id; break }
|
||||
trainer1 = NPCTrainer.new("PLAYER1", t_type)
|
||||
trainer2 = NPCTrainer.new("PLAYER2", t_type)
|
||||
items1 = []
|
||||
items2 = []
|
||||
team1.each_with_index do |p, i|
|
||||
next if !p
|
||||
if p.level != level
|
||||
p.level = level
|
||||
p.calc_stats
|
||||
end
|
||||
items1[i] = p.item_id
|
||||
trainer1.party.push(p)
|
||||
end
|
||||
team2.each_with_index do |p, i|
|
||||
next if !p
|
||||
if p.level != level
|
||||
p.level = level
|
||||
p.calc_stats
|
||||
end
|
||||
items2[i] = p.item_id
|
||||
trainer2.party.push(p)
|
||||
end
|
||||
scene = PokeBattle_DebugSceneNoLogging.new
|
||||
battle = rule.createBattle(scene, trainer1, trainer2)
|
||||
battle.debug = true
|
||||
battle.controlPlayer = true
|
||||
battle.internalBattle = false
|
||||
decision = battle.pbStartBattle
|
||||
team1.each_with_index do |p, i|
|
||||
next if !p
|
||||
p.heal
|
||||
p.item = items1[i]
|
||||
end
|
||||
team2.each_with_index do |p, i|
|
||||
next if !p
|
||||
p.heal
|
||||
p.item = items2[i]
|
||||
end
|
||||
end
|
||||
if decision == 1 # Team 1 wins
|
||||
team1.addMatch(team2, 1)
|
||||
team2.addMatch(team1, 0)
|
||||
elsif decision == 2 # Team 2 wins
|
||||
team1.addMatch(team2, 0)
|
||||
team2.addMatch(team1, 1)
|
||||
else
|
||||
team1.addMatch(team2, -1)
|
||||
team2.addMatch(team1, -1)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user