Michael Lawrence Dee
2015-07-21T00:21:43Z
I have a new problem. I used the format of Breaker never activating in the tutorial and modified it to make AD Changer to never set but it doesn't work.

Also, I have the part in startup that says
decks.boechk = false to check if book of eclipse has been activated during the turn
how do I access it later on outside the startup function?
Now this is how I play:
Snarky
  • Snarky
  • Advanced Member Topic Starter
2015-07-21T14:30:55Z
Originally Posted by: Michael Lawrence Dee 

I have a new problem. I used the format of Breaker never activating in the tutorial and modified it to make AD Changer to never set but it doesn't work.


Thats what the several blacklist propertys are for. The tutorial might be a little confusing, as the first part about Breaker is more how you create an AI from scratch, how the inner workings work etc. Implementing a deck into the existing AI is done a little differently, as shown in the later parts of the tutorial.

For your example, you want to add something like this, taking my previous code as a basis:


function EmptyJarStartup(deck) 
  deck.Init = EmptyJarInit 
  deck.SummonBlacklist = EmptyJarSummonBlacklist
end

EmptyJarSummonBlacklist = {
  96146814, -- A/D Changer
  33508719, -- Morphing Jar
  ...
}


The summon blacklist prevents any inherent normal or special summon or set to be done by the general AI logic for all card IDs on the blacklist, within the parameters of your deck. If you want the cards to be summoned at all, you have to handle their summoning conditions manually, like explained in the first parts of the tutorial.
The blacklist does not prevent the AI to select the monsters for special summoning cards or effects, like Monster Reborn.

There is also a "Set Blacklist", but that only affects Spells&Traps the AI shouldn't set.
Quote:


Also, I have the part in startup that says
decks.boechk = false to check if book of eclipse has been activated during the turn
how do I access it later on outside the startup function?


I would just use a global variable for this. Book of Eclipse will usually be handled in OnSelectChain, so you'd have a logic, somewhat like this:


GlobalEclipse = 0 

function ChainEclipse()
  if ... then -- check for Eclipse activation requirement here
    GlobalEclipse = Duel.GetTurnCount() -- set the global variable
    return true
  end
  return false
end

function EmptyJarOnSelectChain(cards)
  if HasID(cards,35480699,ChainEclipse) then
    return 1,CurrentIndex
  end
end

If you did set it up this way, you can check for the eclipse activation by comparing the current turn count to your global variable:


if GlobalEclipse == Duel.GetTurnCount() then
  -- The AI activated Eclipse this turn
else
  -- The AI did not activate Eclipse this turn
end


Does this help you?
.
Kindrindra
2015-09-11T09:22:53Z
So, I do have one question... How does the deck.Sum function work? When making AI for Synchro decks, I sometimes want to have the AI give certain Tuners or Non-Tuners priority, but I can't figure out how to do that. To give an example, if the AI has a field of Blue-Eyes, Maiden with Eyes of Blue, and The White Stone of Legend, I'd be preferable if the AI summoned Azure-Eyes using White Stone as the Tuner rather than Maiden. But I haven't been able to make it do this, even by having it return "false" when the card.id is Maiden's, or by using the Add function.

Thanks in advance!
Cards scripted: ...lost count
Cards released: 18
Cards illustrated: Double digits
Main Decks: D/D, Heraldic, Gusto on good days
Snarky
  • Snarky
  • Advanced Member Topic Starter
2015-09-11T23:43:04Z
Sum is a little tricky, as it is not always used in this situation, as far as I am aware. Sometimes, a Synchro Summon is handled entirely in OnSelectCard instead. I'll experiment a little and get back to you on this.
Snarky
  • Snarky
  • Advanced Member Topic Starter
2015-09-13T09:11:07Z
Alright, I have looked into the issue a bit. As already stated, one of the problems you would have to manage would be, that, depending on the synchro monster you're about to summon, multiple calls to either OnSelectCard or OnSelectSum are made. For the standard "1 tuner + 1 or more non-tuners", OnSelectCard is called first for the tuner, as that will always select a single card. The non-tuners are handled via OnSelectSum, as those must match a certain level based on the tuner choice. Do note, that this will always call OnSelectSum, even if there is only one possible choice.

The "triggeringCard" parameter for both functions will be "false", by the way, that parameter unfortunately only works for effects, that activate in a chain, not for inherent summons like synchro summons. So you will need a different way to keep track of the card you're about to summon. The AI script usually handles this with a global variable, storing the id of the summoned monster and checking for that in the respective functions.

Back to the specific synchros: There are some synchros, that deviate from the standard "1 tuner + 1 or more non-tuner" formula, those might call the functions in a different order. Take "Vylon Omega" as an example: it specifies "2 tuner + 1 non-tuner "Vylon" monster". 2 tuners is handled by 2 seperate calls to OnSelectCard, 1 non-tuner is also handled by a 3rd call to OnSelectCard, because it specifies to use a single non-tuner as opposed to the "1 or more" wording used by other synchros.

So, my proposed way to go about this would be as follows: You store the id or card, that is about to be summoned in a global variable, which is being reset on the next OnSelectInitCommand call. Then you catch all calls to OnSelectSum and OnSelectCard, that don't have a triggering card parameter while this variable is set, and you have a pretty good catch on your synchro material selection. Or, for your specific issue, a call to OnSelectCard would suffice, since tuner selecting is usually handled there. There might be certain situations, where this might be able to misfire, but we'll worry about those, when they come up.

So something like this:

GlobalSynchroSummon = nil
function SummonStardust(c)
  if ShouldSummonStardust() then
    GlobalSynchroSummon = c
    return true
  end
  return false
end
function MyDeckInit(cards)
  local Sum = cards.summonable_cards
  GlobalSynchroSummon = nil 
  -- when Init is called, all target selection should be done, so its safe to reset here
  if HasID(Sum,44508094,SummonStardust) do
    return COMMAND_SPECIAL_SUMMON,CurrentIndex
  end
end
function MyDeckCard(cards,min,max,id,c)
  if not c and GlobalSynchroSummon then
  -- probably Tuner selection

    -- if you did set up priorities:
    return Add(cards,PRIO_TOGRAVE) 
    --  You would give maiden a lower TOGRAVE priority than stone, so this should always pick stone

    -- if not, you'd have something like this instead:
    for i=1,#cards do
      if cards[i].id == 79814787 then -- White Stone of Legend
        return {i}
      end
    end
  end
end
This code is untested, it might not work exactly like that. Its just meant to showcase, how to work around the issue.

Does this help you in any way?
Kindrindra
2015-09-14T22:12:39Z
Perfect, thanks a lot! ^_^

I wouldn't have expected it to work like that, but it does make sense in retrospect.
Cards scripted: ...lost count
Cards released: 18
Cards illustrated: Double digits
Main Decks: D/D, Heraldic, Gusto on good days
Snarky
  • Snarky
  • Advanced Member Topic Starter
2015-10-01T12:50:20Z
Updated the tutorial, it does now contain most of the information on how to add your own deck to the standard AI from this thread .

The tutorial is pretty much done now, I think. Anything important, that I missed? I know, that it is very long and confusing, but thats pretty much the nature of scripting for the YGOPro AI anyways 😃
neftalimich
2015-10-26T15:12:51Z
Hello Snarky, first, I must thank you for all your work. It is admirable what you've done. Thank you very much. It give me a lot of fun time.

I am a software developer, and is the first time I am trying this, at first is overwhelming to see the code for the different decks. I'll give it time to try something.

And I have a question, I try to deal with "U.A. Penalty Box". I have this:



function SelectAttackConditions(c,source)
-- ...
if c.setcode == 178 then -- U.A. Monster
    return AttackUAMonster(c,source)
  end
end

function AttackUAMonster(c,source)
  return not HasID(OppST(),70043345,true) -- U.A. Penalty Box on the field
  or HasID(Field(),51452091,true)  -- Royal Decree
  or Get_Card_Count_Pos(AIMon(),POS_FACEUP_ATTACK) <= 1 -- This can be improved
  or Can_Activate(OppST(),'70043345',true) -- Something like this
end

So... How I can know if "U.A. Penalty Box" effect was activated that turn? or better yet, how I can know if "U.A. Penalty Box" can activate its effect?

Sorry if I do not use the tools well, its my first try. And sorry for my english, is not my native language.

Y de antemano, muchas gracias 🙂
Snarky
  • Snarky
  • Advanced Member Topic Starter
2015-10-27T00:47:57Z
Originally Posted by: neftalimich 

Hello Snarky, first, I must thank you for all your work. It is admirable what you've done. Thank you very much. It give me a lot of fun time.



Hey, thank you very much.

Quote:


I am a software developer, and is the first time I am trying this, at first is overwhelming to see the code for the different decks. I'll give it time to try something.



Great to see more and more people trying. Take your time :)

Quote:

So... How I can know if "U.A. Penalty Box" effect was activated that turn? or better yet, how I can know if "U.A. Penalty Box" can activate its effect?



Unfortunately, I do not know of a way to check, if a specific effect can be activated this turn. So you have to handle that manually.

For that, I use the OPTCheck() and OPTSet() functions. You can pass an ID for a hard OPT ("You can only use this effect once per turn...", or a card for a soft OPT check ("Once per turn, you can...")

I assume, you have some sort of code already set up for chaining Penalty Box' effect in OnSelectChain() and OnSelectEffectYesNo()? If this is the case, you can set up the OPTCheck like this:


function UAChain(cards)
  if HasID(cards,70043345,ChainPenaltyBox) then
    OPTSet(70043345)
    return 1,CurrentIndex
  end
end

"SelectAttackConditions" is used to check, whick conditions have to be checked for the AI to attack INTO certain monsters. You could use it to prevent your opponent from attacking into UAs with Penalty Box on the field, for example. But thats probably too specific and unneeded for now.

Have a look at the "BattleCommand" and "AttackTarget" functions instead. These are used to handle situations, when certain AI monsters should attack into the player's monsters with a custom targeting logic, ignoring the base ATK calculation. You can set them up for your deck in the initial function as usual:

function UAStartup(deck)
  deck.AttackTarget         = UAAttackTarget
  deck.AttackBoost          = UAAttackBoost
end

Then, the actual functions could be handled like this:

function PenaltyBoxFilter(c)
  return Affected(c,TYPE_TRAP)
end
function UABattleCommand(attackers,targets,act)
  SortByAtk(attackers) -- lowest ATK first
  for i=1,#attackers do
    local c = attackers[i]
    if HasIDNotNegated(AIST(),70043345,true) 
    and OPTCheck(70043345)
    and CardsMatchingFilter(targets,PenaltyBoxFilter)>0
    and FilterSet(c,178)
    then
      return Attack(i)
    end
  end
end
function UAAttackTarget(targets,attacker)
  if HasIDNotNegated(AIST(),70043345,true) 
  and OPTCheck(70043345)
  and FilterSet(attacker,178)
  then
    return BestTarget(targets,1,PRIO_BANISH,PenaltyBoxFilter)
  end
end

A few notes, that might be helpful (didn't want to spam the code with comments):
- BattleCommand determines, if an attack order should be given at all, and which card attacks first
- SortByAtk sorts the attackers in ascending order, so the lowest ATK is checked first. Should result in the lowest UA being used to trigger Box, so higher ATKs can be used for more damage. If you return true as a 2nd parameter, order would be descending instead.
- AttackTarget selects the target of the current attacker. I use BestTarget instead of BestATtackTarget here, because BestAttackTarget uses the attack points logic. This would result in attacking a monster we could already beat in battle. BestTarget chooses the strongest target matching the filter instead, which probably makes more sense.
- Using c.setcode == 178 is not recommended. Some cards are part of multiple archetypes or sub-archetypes, in which case a simple comparison leads to wrong results. You can do a bit32.band(c.setcode,178)>0, but that can be inaccurate as well. I usually use the FilterSet() or the IsSetCode() functions.

Quote:


Sorry if I do not use the tools well, its my first try.


Don't apologize. It is complex and confusing, and the tools are not always the most logical things to use. Just keep trying and keep asking, if something comes up :)

Does this help you?

.
neftalimich
2015-11-03T14:39:28Z
Thank you very much snarky, and sorry for my slow reply, I have been busy, I will look and continue learning. You are a genius. 😃
Kindrindra
2015-12-18T12:01:45Z
Not sure if this is just something that was changed while I wasn't paying attention, or that I missed or somesuch, but AI.Chat() doesn't seem to work anymore, at least for me. I tried reinstalling a clean program, but it still doesn't seem to want to work for me. I was trying to put some Summoning Chants in an AI ([:blush:]), and then realized that it wasn't working for any of the old decks either (like how AI_Wizard announces what it is at the start of the duel). Is it just me, or is there some new equivalent or something?
Cards scripted: ...lost count
Cards released: 18
Cards illustrated: Double digits
Main Decks: D/D, Heraldic, Gusto on good days
Snarky
  • Snarky
  • Advanced Member Topic Starter
2015-12-18T13:22:43Z
It seems to work on my end. The standard AI always announces the AI script version at the start of the duel ([AI]Impulse: AI script version 0.32), does that not work for you either?
Kindrindra
2015-12-18T14:12:40Z
Yeah, when I start a duel I get:
[System]: AI version 0.9.0 beta
[System]: Give feedback and report bugs at http://www.ygopro.co

And that's it from start to end of duel. It's quite bizarre.
Cards scripted: ...lost count
Cards released: 18
Cards illustrated: Double digits
Main Decks: D/D, Heraldic, Gusto on good days
Kindrindra
2015-12-20T02:20:50Z
On an unrelated note, is there any pre-existing function to check if the AI has an available Normal Summon? I could always use a Global Variable and just set it to false whenever the script orders a Normal Summon (and reset it each turn), but that just sounds so... sloppy?
Cards scripted: ...lost count
Cards released: 18
Cards illustrated: Double digits
Main Decks: D/D, Heraldic, Gusto on good days
Snarky
  • Snarky
  • Advanced Member Topic Starter
2015-12-25T11:54:13Z
Originally Posted by: Kindrindra 

On an unrelated note, is there any pre-existing function to check if the AI has an available Normal Summon? I could always use a Global Variable and just set it to false whenever the script orders a Normal Summon (and reset it each turn), but that just sounds so... sloppy?



There is a card script function you can use:


Duel.GetActivityCount(player,ACTIVITY_NORMAL_SUMMON)

It returns the amount of normal summons performed by the player this turn. There are custom wrappers available as well, mostly for backwards compatibility:


NormalSummonCheck(player)
NormalSummonCount(player)

Do note, that NormalSummonCheck returns true, if the player did summon this turn. The other way around might have been better, but thats what I went for. It also doesn't take additional normal summons into account, those you have to manage manually.
neftalimich
2016-03-09T16:42:30Z
Hi Snarky, i have a question:

In the opponent's turn, how can chain an effect only when does not start a chain?, it means, a chain when the opponent set a card or summon without effect monster, or attack, etc. The reason is because i don't want to lose the timing, my effect always must be chain 1.

Thanks and sorry for my bad english.
Snarky
  • Snarky
  • Advanced Member Topic Starter
2016-03-09T18:27:52Z
Originally Posted by: neftalimich 

Hi Snarky, i have a question:

In the opponent's turn, how can chain an effect only when does not start a chain?, it means, a chain when the opponent set a card or summon without effect monster, or attack, etc. The reason is because i don't want to lose the timing, my effect always must be chain 1.

Thanks and sorry for my bad english.



You can use the function Duel.GetCurrentChain() for this. I use it for the same reasons you stated for a couple of cards. It returns a number, which is equivalent to the current chain link of the current chain (duh).

So you would do something like this:


function ChainMyCard()
  if Duel.GetCurrentChain() == 0 then
     return true
  end
  return false
end

Does this help you?
neftalimich
2016-03-10T20:27:49Z
Originally Posted by: Snarky 

Originally Posted by: neftalimich 

Hi Snarky, i have a question:

In the opponent's turn, how can chain an effect only when does not start a chain?, it means, a chain when the opponent set a card or summon without effect monster, or attack, etc. The reason is because i don't want to lose the timing, my effect always must be chain 1.

Thanks and sorry for my bad english.



You can use the function Duel.GetCurrentChain() for this. I use it for the same reasons you stated for a couple of cards. It returns a number, which is equivalent to the current chain link of the current chain (duh).

So you would do something like this:


function ChainMyCard()
  if Duel.GetCurrentChain() == 0 then
     return true
  end
  return false
end

Does this help you?




it works!!, thank you so much,
and where I can find the list that contains all functions?
Snarky
  • Snarky
  • Advanced Member Topic Starter
2016-03-10T21:24:55Z
Originally Posted by: neftalimich 


and where I can find the list that contains all functions?



It does not really exist, unfortunately. The specific AI functions are documented in the ai-template.lua as stated in the tutorial, a lot of the card functions can be found on fluorohydrite's GitHub , but without proper documentation and parameter lists. I tried to outline the most important or useful ones in the tutorial, but I don't want to cover all of them.

At some point, I might do a full API, including my own custom functions as well. Until then, you can use the GitHub list, the template and this tutorial to hopefully find out, what you need. And if you don't, just check back and ask.
Rafael_AD
2016-04-04T08:29:13Z
I might try to make my own script for sylvan and frightfur decks. However, I can't seem to make it work for sylvan komushroomo to always excavate 5 cards. Where and how do I handle that?
Users browsing this topic