[Advanced AI][FlatyCloud] SpellPriorityAI

    Publicités

Users Who Are Viewing This Thread (Total: 0, Members: 0, Guests: 0)

Lmoony

Membre
Apr 7, 2019
28
56
214
30
Bonjour, voici ma nouvelle IA.

Code:
DofusClass="9" -- CRA

-- ne pas toucher ces 2 lignes
local CellArray = {}
local CellArrayInit = 0

local Spells = {
    [1] = {
        name = "Fleche de Recul", -- nom du sort
        maxRange = 4, -- la portee du sort (Sans aucun bonus)
        canRangeBeBoosted = false, -- si le sort a une portee modifiable
        apCost = 3, -- le cout en PA du sort
        maxShots = 2, -- le nombre d'utilisation maximum en un tour
        maxShotsPerTarget = 1, -- le nombre d'utilisation maximum par tour par cible
    },
    [2] = {
        name = "Fleche Magique",
        maxRange = 8,
        canRangeBeBoosted = true,
        apCost = 3,
        maxShots = 3,
        maxShotsPerTarget = 2,
    },
}

local distToFlee = 8 -- en dessous de cette distance le bot tentera de reculer en fin de tour, sinon, il tentera d'avancer.

function InitCellsArray()
    local b = 0
    local startX = 0
    local startY = 0
    local cell = 0
    local a = 0

    while (a < 20) do
        b = 0
        while (b < 14) do
            CellArray[cell] = {x = startX + b, y = startY + b}
            cell = cell + 1
            b = b + 1
        end
        startX = startX + 1
        b = 0
        while (b < 14) do
            CellArray[cell] = {x = startX + b, y = startY + b}
            cell = cell + 1
            b = b + 1
        end
        startY = startY - 1
        a = a + 1
    end
end

function CellIdToCoord(cellId)
    if (not(CellArrayInit == 1)) then
        InitCellsArray()
    end
    CellArrayInit = 1
    if (IsCellIdValid(cellId)) then
        return CellArray[cellId]
    end
    return nil
end

function TableFilter(tbl, func)
     local newtbl= {}
     for i, v in pairs(tbl) do
         if func(v) then
             table.insert(newtbl, v)
         end
     end
     return newtbl
end

function GetIndex(tbl, obj)
    for i, v in ipairs(tbl) do
        if v == obj then
            return i
        end
    end
    return nil
end

function CoordToCellId(coord)
    return math.floor((((coord.x - coord.y) * 14) + coord.y) + ((coord.x - coord.y) / 2))
end

function IsCellIdValid(cellId)
    return (cellId >= 0 and cellId < 560)
end

function ManhattanDistanceCellId(fromCellId, toCellId)
    local fromCoord = CellIdToCoord(fromCellId)
    local toCoord = CellIdToCoord(toCellId)
    if fromCoord ~= nil and toCoord ~= nil then
        return (math.abs(toCoord.x - fromCoord.x) + math.abs(toCoord.y - fromCoord.y))
    end
    return nil
end

function ManhattanDistanceCoord(fromCoord, toCoord)
    return (math.abs(toCoord.x - fromCoord.x) + math.abs(toCoord.y - fromCoord.y))
end

function GetAllEntitiesArray()
    local entities = fight:getAllEntities()
    local ret = {}

    for i = 0, fight:getEntitiesCount() -1 do
        table.insert(ret, entities[i])
    end
    return ret
end

function FindShotsBySpellForEntity(memory, spell, entity)
    if #memory == 0 then
        return nil
    end
    for _, v in ipairs(memory) do
        if v.s == spell and v.e == entity then
            return v
        end
    end
    return nil
end

function AddShotBySpellForEntity(memory, spell, entity)
    local foundEntry = FindShotsBySpellForEntity(memory, spell, entity)
    if foundEntry ~= nil then
        foundEntry.count = foundEntry.count + 1
    else
        table.insert(memory, {s = spell, e = entity, count = 1})
    end
end

function GetReachableCellIds()
    local coord = CellIdToCoord(fighter:getCellId())
    local mp = fighter:getMP()
    local ret = {}

    for i = -mp, mp do
        local d = mp - math.abs(i)
        if d == 0 then
            local newCoord = {x = coord.x + i, y = coord.y}
            local newCellId = CoordToCellId(newCoord)
            if IsCellIdValid(newCellId) and fight:isCellWalkable(newCellId) and fight:isFreeCell(newCellId) then
                table.insert(ret, newCellId)
            end
        else
            for j = d * -1, d do
                local newCoord = {x = coord.x + i, y = coord.y + j}
                local newCellId = CoordToCellId(newCoord)
                if IsCellIdValid(newCellId) and fight:isCellWalkable(newCellId) and fight:isFreeCell(newCellId) then
                    table.insert(ret, newCellId)
                end
            end
        end
    end
    return ret
end

function GetCellIdToAttackFrom(cellIdToAttack, spell)
    local reachableCellIds = GetReachableCellIds()
    local spellTotalRange

    if spell.canRangeBeBoosted then
        spellTotalRange = spell.maxRange + fighter:getRange()
    else
        spellTotalRange = spell.maxRange
    end
    reachableCellIds = TableFilter(reachableCellIds, function(a)
        return IsCellIdValid(a) and IsCellIdValid(cellIdToAttack) and ManhattanDistanceCellId(a, cellIdToAttack) <= spellTotalRange
    end)
    if #reachableCellIds < 1 then
        return nil
    end
    if #reachableCellIds >= 2 then
        table.sort(reachableCellIds, function(a, b)
            return (fight:getDistance(fighter:getCellId(), a) < fight:getDistance(fighter:getCellId(), b))
        end)
    end
    for _, v in ipairs(reachableCellIds) do
        if fight:test(spell.name, v, cellIdToAttack) then
            return v
        end
    end
    return nil
end

function PickATarget(entitiesToPickFrom, spell)
    local spellTotalRange
    if spell.canRangeBeBoosted then
        spellTotalRange = spell.maxRange + fighter:getRange()
    else
        spellTotalRange = spell.maxRange
    end
    local filteredEntities = TableFilter(entitiesToPickFrom, function(a)
        return a.Alive and IsCellIdValid(a.CellId) and ManhattanDistanceCellId(fighter:getCellId(), a.CellId) <= spellTotalRange + fighter:getMP()
    end)
    if #filteredEntities < 1 then
        return nil, nil
    end
    if #filteredEntities >= 2 then
        table.sort(filteredEntities, function(a, b)
            return (fight:getDistance(fighter:getCellId(), a.CellId) < fight:getDistance(fighter:getCellId(), b.CellId))
        end)
    end
    for _, v in ipairs(filteredEntities) do
        local bestOption = GetCellIdToAttackFrom(v.CellId, spell)
        if bestOption ~= nil then
            return v, bestOption
        end
    end
    return nil, nil
end

function debug_print_memory(memory)
    if #memory ~= 0 then
        for _, v in ipairs(memory) do
            util:debug('s = ' .. v.s.name .. ' | e = ' .. tostring(v.e) .. ' | count = ' .. v.count)
        end
    end
end

function Main()
    local target
    local bestOption
    local d
    local shotsMemory = {}

    for _, v in ipairs(Spells) do
        if fighter:getAP() >= v.apCost then
            local allEntities = GetAllEntitiesArray()
            local countShots = 0
            while countShots < v.maxShots and fighter:getAP() >= v.apCost do
                -- debug_print_memory(shotsMemory)
                -- util:debug('Looking for a target for ' .. v.name)
                target, bestOption = PickATarget(allEntities, v)
                if target == nil then -- le sort n'est pas utilsable en l'etat
                    -- util:debug('Did not find one')
                    break -- on passe au sort suivant
                else
                    -- util:debug('Found one : ' .. target:GetName() .. ' (lvl. ' .. target.Level .. ' ) at a distance of ' .. fight:getDistance(fighter:getCellId(), target.CellId))
                    if bestOption ~= fighter:getCellId() then
                        fight:moveToCell(bestOption)
                    end
                    local debug = fight:launchSpellInCell(target.CellId, v.name)
                    if debug ~= 1 then -- si le lance du sort n'a pas fonctionne, quelle que soit la raison
                        break -- on passe au sort suivant
                    end
                    -- sinon
                    countShots = countShots + 1
                    AddShotBySpellForEntity(shotsMemory, v, target)
                    if FindShotsBySpellForEntity(shotsMemory, v, target).count >= v.maxShotsPerTarget then
                        local tmp = GetIndex(allEntities, target)
                        if tmp ~= nil then
                            table.remove(allEntities, tmp)
                        end
                    end
                end
            end
        end
    end
    d = ManhattanDistanceCellId(fighter:getCellId(), fight:getNearestEnemy())
    if d ~= nil then
        if d > distToFlee then
            fight:moveToWardCell(fight:getNearestEnemy())
        else
            fight:moveFarthestCell(fight:getNearestEnemy())
        end
    end
    fighter:passTurn()
end

Avant toute chose, cette IA ne fonctionne que sur la version FlatyCloud du bot.

Cette nouvelle IA se sert d'une configuration de sorts par priorité pour attaquer l'ennemi de la manière la plus performante possible en suivant le schéma suivant.
  1. Trouver la cible la plus proche pour le sort ayant la priorité [1]
  2. Trouver la meilleure ligne de vue a portée de PM pour cette cible. Si pas possible, retour au point 1 avec le sort de priorité [2], et ainsi de suite
  3. Si ligne de vue trouvée, se rendre sur la cellule permettant la ligne de vue et attaquer.
  4. Si échec de l'attaque (Raison inconnue), retour au point 1 avec le sort de priorité inférieure
  5. Si tir réussi, retour au point 1 avec le meme sort.
  6. Reculer si l'ennemi est trop proche, avancer s'il est trop loin
  7. Passer son tour
Je précise que si le bot ne trouve aucune cible a tous ses sorts, il se retrouve direct au point 6.

Voici l'exemple de configuration que vous retrouverez en haut du fichier lua.
Code:
local Spells = {
    [1] = {
        name = "Fleche de Recul",  -- nom du sort
        maxRange = 4, -- la portee du sort (Sans aucun bonus)
        canRangeBeBoosted = false, -- si le sort a une portee modifiable
        apCost = 3, -- le cout en PA du sort
        maxShots = 2, -- le nombre d'utilisation maximum en un tour
        maxShotsPerTarget = 1, -- le nombre d'utilisation maximum par tour par cible
    },
    [2] = {
        name = "Fleche Magique",
        maxRange = 8,
        canRangeBeBoosted = true,
        apCost = 3,
        maxShots = 3,
        maxShotsPerTarget = 2,
    },
}

local distToFlee = 8 -- en dessous de cette distance le bot tentera de reculer en fin de tour, sinon, il tentera d'avancer.
Tous les champs sont necessaires ! (Si le sort n'a pas de valeur en utilisation maximum ou en utilisation maximum par cible, choisissez-en une arbitraire)
Vous pouvez rentrer plus de sorts que 2 si vous le désirez. (Plus il y en as, plus la charge de calcul est grande, n'abusez pas)
Si cela n'est pas déja evident, la priorité se matérialise par l'ordre dans lequel ils sont décrit dans la configuration. Le premier est le plus prioritaire.
Les sorts qui possèdent une portée minimum ne sont pas encore géré mais le seront a la prochaine mise a jour de l'IA.

Pensez a adapter la premiere ligne du script en fonction de la classe que vous decidez d'utiliser.
Code:
DofusClass="9" -- CRA
Code:
1 - Feca
2 - Osamodas
3 - Enutrof
4 - Sram
5 - Xelor
6 - Ecaflip
7 - Eniripsa
8 - Iop
9 - Cra
10 - Sadida
11 - Sacrieur
12 - Pandawa
13 - Roublard
14 - Zobal
15 - Steamer
16 - Eliotrope
17 - Huppermage
18 - Ouginak

Merci de me tenir au courant d’éventuels bugs et n'hésitez pas a partager vos configurations de sorts qui fonctionnent bien!
 

Attachments

  • SpellPriorityAI 1.2.lua
    7.2 KB · Views: 429
Last edited:
  • Like
  • Love
Reactions: Oxygeddon, Rokusasuu, Bazoulax and 10 others

Gryffin

Membre
Oct 25, 2016
8
0
431
29
Tu gère de ouf avec tes IA, merci pour le partage et très beau travail ! :)
Post automatically merged:

Yo, je comprend pas tout, l'ia ne fait que de se déplacer mais ne lance pas de sorts, j'ai un crâ air donc je n'ai rien touché dans le fichier et j'ai flatycloud :)
 
Last edited:

Lmoony

Membre
Apr 7, 2019
28
56
214
30
Il y a des modifications à faire dans la plus part des scripts IA un peu avancés.
C'est lié au changement d'interprêteur lua au sein du bot.
 

Gryffin

Membre
Oct 25, 2016
8
0
431
29
Ok, j'ai pas tout compris, quelles sont les choses a modifier ? parce que les données des sorts ont l'air correctes par rapport à mes sorts :)
 

Lmoony

Membre
Apr 7, 2019
28
56
214
30
Update pour le récent changement de l’interpréteur Lua du bot.
Le script n'utilisait plus ses sorts. Fix
 

Attachments

  • SpellPriorityAI 1.2.lua
    7.2 KB · Views: 163
Last edited:

Striife

Membre
Apr 6, 2020
19
2
124
28
Hello, pas moyen de lancer de sort malgré la version fix, j'ai modifié pour un enu, mais j'ai du faire une bêtise ? ci joint, mes modifs
 

Attachments

  • SpellPriorityAI 1.2.lua
    7.2 KB · Views: 27

Lmoony

Membre
Apr 7, 2019
28
56
214
30
Ta version prétendue modifiée est celle que j'ai postée semble-t-il. Peut-être t'es-tu trompé de fichier?
 
Last edited:

Striife

Membre
Apr 6, 2020
19
2
124
28
My bad, j'ai mis des underscore dans le nom du spell... c'est good :) merci
 
  • Like
Reactions: malusdefou

malusdefou

Membre actif
May 26, 2013
118
2
924
Undefined
Yo :),

Il est possible de configurer un sort de sorte à ce qu'il soit lancé sur le personnage lui-même (ex: Rempart féca, boosts crâ etc ...) ?
 

Lmoony

Membre
Apr 7, 2019
28
56
214
30
Je travaille sur une version sur laquelle c'est plus possible oui, pas encore prête cependant
 
  • Love
  • Like
Reactions: Meetic and malusdefou

astralman

Membre
May 16, 2012
7
1
723
Hello !

Merci pour le partage, c'est un super travail.

Envisages-tu d'ajouter une variable minimum de portée pour un sort ? Certains on des portées comprises entre 3 et 5 par exemple.

A bientot!
 
  • Like
Reactions: malusdefou

neoxlgt

Membre
Mar 30, 2020
11
1
4
28
Discord
neoxlgt #0356
Salut à toi, déjà je tiens à te remercier pour ton travail car c'est super j'aime beaucoup le concept qui permet de faire des combats de manière vraiment optimisé et intelligent.

Cependant j'ai notifié que le temps entre deux sorts peut être un peu long à mon gout, est ce possible de d'améliorer ce délai ?

Je reste sur une ia brute qui fonce dans le tas et lance les sorts puisque je oneshot ceux que je veux, mais j'utiliserai le tiens pour des combats plus compliqué, j'attends la suite de tes versions :)
 
  • Like
Reactions: malusdefou

Lmoony

Membre
Apr 7, 2019
28
56
214
30
La pause marquée entre le lancement de deux sort n'est pas sensée être très longue avec ce script qui reste relativement simple.

A chaque tour, l'IA va calculer le lancement des sorts en simulant le lancement depuis "quasi" toutes les cases accessibles a tes PM pour vérifier la ligne de vue.
Pour cette raison, avoir un personnage qui possède 6 PM prendra légèrement plus de temps qu'un personnage a 3 PM pour jouer. (Mais c'est négligeable)

Afin que je puisse étudier la situation plus en profondeur, pourrais-tu me renseigner sur la classe et la configuration de sort que tu utilisais lorsque tu as constaté une lenteur?

Merci
 

vissounight

Membre Banni
Sep 28, 2019
119
217
244
Discord
Vissou 6Night
Moi je voudrais bien un logiciel pour créer ses propre IA et de partagez, car je joue sur temporis enu Feu du coup les sort feu sont différent j'ai les même sort que le IA Iop Feu avec Orage ect.. Sinon personne ne veut créer de IA ENU FEU TEMPORIS LEVEL 50 MAX ?
 

Lmoony

Membre
Apr 7, 2019
28
56
214
30
Merci de supprimer ton message qui n'a rien a voir avec le sujet.
 
  • Like
Reactions: Hofmann87

malusdefou

Membre actif
May 26, 2013
118
2
924
Undefined
La pause marquée entre le lancement de deux sort n'est pas sensée être très longue avec ce script qui reste relativement simple.

A chaque tour, l'IA va calculer le lancement des sorts en simulant le lancement depuis "quasi" toutes les cases accessibles a tes PM pour vérifier la ligne de vue.
Pour cette raison, avoir un personnage qui possède 6 PM prendra légèrement plus de temps qu'un personnage a 3 PM pour jouer. (Mais c'est négligeable)

Afin que je puisse étudier la situation plus en profondeur, pourrais-tu me renseigner sur la classe et la configuration de sort que tu utilisais lorsque tu as constaté une lenteur?

Merci

Je fais le même constat par rapport à la latence lors d'une prise de décision parfois, j'ai un script simple pour crâ personnellement (priorité qui va de flèche de recul -> flèche magique -> flèche harcelante), le plupart des tours prennent un peu de temps à être joués, comparé à une IA bourine, si tu veux je peux te joindre le script ...
 

Striife

Membre
Apr 6, 2020
19
2
124
28
Hello,

Merci encore pour le partage de cette IA, une petite question, est-ce que les boosts sont gérés ? si oui comment svp ?

EDIT :Je viens de voir ton message plus haut concernant les boosts my bad, bon courage a toi, en esperant voir une nouvelle version soon

Merci
Post automatically merged:

Du coup j'ai rajouté les boosts en deuspi :) du moins pour cra, ... have fun
 

Attachments

  • SpellPrio+Boosts(Striife edit).lua
    7.6 KB · Views: 83
Last edited:

Striife

Membre
Apr 6, 2020
19
2
124
28
Petite remarque : il semblerait qu'en groupe, il ai du mal à gérer ses alliés, de temps en temps il essaye de taper à travers sans succès.
 

Lmoony

Membre
Apr 7, 2019
28
56
214
30
Le problème est que FlatyCloud/FlatyBot lui-même ne gère pas bien les alliés.
Pour te donner un exemple, la fonction fight:getAllEntities() ne renvoi que les ennemis. (pas les alliés, ni l'entité même de ton personnage)
Il n'est donc pas impensable de penser que la fonction fight:test() (Dont le rôle est de dire si oui on non, la ligne de vue est possible) ait le même genre de soucis.
Dans cette IA, toutes les décisions passent par cette fonction.
Si tu l'as constatée en train d'essayer de lancer un sort a travers un allié, c'est sans doute parce que fight:test() a dit que c’était possible alors que ça ne l'était pas.
Or, cette fonction, je ne peux pas la modifier/corriger car elle ne m'appartient pas et que je n'y ai pas accès.
Je ne peux donc rien y faire, désolé.
 
Last edited:
  • Sad
Reactions: iveu and malusdefou

malusdefou

Membre actif
May 26, 2013
118
2
924
Undefined
Le problème est que FlatyCloud/FlatyBot lui-même ne gère pas bien les alliés.
Pour te donner un exemple, la fonction fight:getAllEntities() ne renvoi que les ennemis. (pas les alliés, ni l'entité même de ton personnage)
Il n'est donc pas impensable de penser que la fonction fight:test() (Dont le rôle est de dire si oui on non, la ligne de vue est possible) ait le même genre de soucis.
Dans cette IA, toutes les décisions passent par cette fonction.
Si tu l'as constatée en train d'essayer de lancer un sort a travers un allié, c'est sans doute parce que fight:test() a dit que c’était possible alors que ça ne l'était pas.
Or, cette fonction, je ne peux pas la modifier/corriger car elle ne m'appartient pas et que je n'y ai pas accès.
Je ne peux donc rien y faire, désolé.

Ta essayé cette fonction :) ? : Ce lien n'est pas visible, veuillez vous connecter pour l'afficher. Je m'inscris!
 

Lmoony

Membre
Apr 7, 2019
28
56
214
30
Cette fonction ne peut pas aider car elle ne fait que renvoyer la case d'un allié quelconque à portée du sort reçu en paramètre. Elle ne permet en aucun cas de mapper la position des alliés.
Je rajouterais que, même si cette fonction le permettait, cela ne résoudrait en rien le problème.
En effet, je l'ai expliqué, le problème vient d'une fonction qui ne m'appartient pas. C'est au sein de cette fonction que certains changements doivent être opérés.
Même si moi-même dans mon code, je prenais en compte les alliés, cela ne changerait en rien le fait que cette fonction, elle, ne les prend pas en compte.

J'espère que tu comprends qu'en cela, rien ne peut résoudre le problème si ce n'est de réécrire toute la logique de cette fonction moi-même en lua et ainsi pouvoir y opérer les changements que j'estimerais nécessaire.
C'est un peu ce que fait la première version de LOSseeker, mais en moins bien.

Cette fonction serait assez verbeuse à réécrire et sans doute assez lente en lua, donc je ne le fais pas.

L'autre solution serait de faire part de ce bug sur le trello en expliquant bien les détails et éventuellement linker ce sujet en supplément.

Si cette solution voit un jour le jour, je n'aurai pas a changer la moindre ligne de code dans l'IA pour qu'elle se mette a gérer les lignes de vue bloquées par les alliés.

J'espère que c'est suffisamment clair.
 
Last edited:
  • Like
Reactions: malusdefou

malusdefou

Membre actif
May 26, 2013
118
2
924
Undefined
Cette fonction ne peut pas aider car elle ne fait que renvoyer la case d'un allié quelconque à portée du sort reçu en paramètre. Elle ne permet en aucun cas de mapper la position des alliés.
Je rajouterais que, même si cette fonction le permettait, cela ne résoudrait en rien le problème.
En effet, je l'ai expliqué, le problème vient d'une fonction qui ne m'appartient pas. C'est au sein de cette fonction que certains changements doivent être opérés.
Même si moi-même dans mon code, je prenais en compte les alliés, cela ne changerait en rien le fait que cette fonction, elle, ne les prend pas en compte.

J'espère que tu comprends qu'en cela, rien ne peut résoudre le problème si ce n'est de réécrire toute la logique de cette fonction moi-même en lua et ainsi pouvoir y opérer les changements que j'estimerais nécessaire.
C'est un peu ce que fait la première version de LOSseeker, mais en moins bien.

Cette fonction serait assez verbeuse à réécrire et sans doute assez lente en lua, donc je ne le fais pas.

L'autre solution serait de faire part de ce bug sur le trello en expliquant bien les détails et éventuellement linker ce sujet en supplément.

Si cette solution voit un jour le jour, je n'aurai pas a changer la moindre ligne de code dans l'IA pour qu'elle se mette a gérer les lignes de vue bloquées par les alliés.

J'espère que c'est suffisamment clair.

Mais si je comprend bien, une IA simple saurait localiser les alliés et est donc adaptée aux combats de groupes, mais celle-ci est plus adaptée pour un personnage en solo de part le fait qu'elle peut rester figée en essayant de tirer à travers les alliés ?