smogon / pokemon-showdown

Pokémon battle simulator.
https://pokemonshowdown.com
MIT License
4.75k stars 2.78k forks source link

Invalid choices should abort Side#choose() #5399

Closed scheibo closed 5 years ago

scheibo commented 5 years ago

In this doubles battle scenario, the p2 has 1 mon remaining, so sends '>p2 move 3 2, pass', only to get back 2 choice errors:

sideupdate
p2
|request|{"active":[{"moves":[{"move":"Transform","id":"transform","pp":15,"maxpp":16,"target":"normal","disabled":false},{"move":"Dragon Dance","id":"dragondance","pp":20,"maxpp":20,"target":"self","disabled":false},{"move":"Crabhammer","id":"crabhammer","pp":15,"maxpp":16,"target":"normal","disabled":false},{"move":"Sleep Talk","id":"sleeptalk","pp":15,"maxpp":16,"target":"self","disabled":false}],"maybeDisabled":true},{"moves":[{"move":"Horn Attack","id":"hornattack","pp":38,"maxpp":40,"target":"normal","disabled":false},{"move":"Inferno","id":"inferno","pp":5,"maxpp":8,"target":"normal","disabled":false},{"move":"Air Cutter","id":"aircutter","pp":38,"maxpp":40,"target":"allAdjacentFoes","disabled":false},{"move":"Leech Life","id":"leechlife","pp":21,"maxpp":24,"target":"normal","disabled":false}]}],"side":{"name":"Bot 2","id":"p2","pokemon":[{"ident":"p2: Jolteon","details":"Jolteon, L55, M","condition":"78/160 tox","active":true,"stats":{"atk":100,"def":89,"spa":140,"spd":129,"spe":150},"moves":["transform","dragondance","crabhammer","sleeptalk"],"baseAbility":"voltabsorb","item":"belueberry","pokeball":"pokeball"},{"ident":"p2: Barboach","details":"Barboach, L55, M","condition":"0 fnt","active":true,"stats":{"atk":83,"def":64,"spa":81,"spd":55,"spe":113},"moves":["hornattack","inferno","aircutter","leechlife"],"baseAbility":"lightmetal","item":"ringtarget","pokeball":"pokeball"},{"ident":"p2: Dusclops","details":"Dusclops, L56, F","condition":"0 fnt","active":false,"stats":{"atk":117,"def":167,"spa":106,"spd":180,"spe":64},"moves":["razorwind","doubleslap","conversion","magmastorm"],"baseAbility":"flamebody","item":"skullfossil","pokeball":"pokeball"},{"ident":"p2: Golem","details":"Golem, L91, F","condition":"0 fnt","active":false,"stats":{"atk":251,"def":271,"spa":125,"spd":146,"spe":106},"moves":["leafstorm","foulplay","bulkup","endeavor"],"baseAbility":"poisonheal","item":"steelgem","pokeball":"pokeball"},{"ident":"p2: Zigzagoon","details":"Zigzagoon, L55, M","condition":"0 fnt","active":false,"stats":{"atk":46,"def":67,"spa":74,"spd":79,"spe":107},"moves":["smellingsalts","fireblast","strength","switcheroo"],"baseAbility":"battlearmor","item":"aerodactylite","pokeball":"pokeball"},{"ident":"p2: Binacle","details":"Binacle, L55, M","condition":"0 fnt","active":false,"stats":{"atk":74,"def":85,"spa":72,"spd":103,"spe":65},"moves":["powerswap","softboiled","naturepower","irontail"],"baseAbility":"sheerforce","item":"heavyball","pokeball":"pokeball"}]}}

sideupdate
p2
|callback|cant|p2a: Jolteon||dragondance

sideupdate
p2
|error|[Invalid choice] Can't move: Jolteon's Dragon Dance is disabled

sideupdate
p2
|error|[Invalid choice] Can't pass: Your Jolteon must make a move (or switch)

'pass' is the correct choice for the p2b slot, given the active Barboach is fainted. I believe https://github.com/Zarel/Pokemon-Showdown/blob/master/sim/side.ts#L695-L763 is the problematic code, as it should track the index in the choiceString to know which active Pokemon the choice applies to.

Full input log for repro:

[gen6doublescustomgame] P:0 (302/829) I:1 (111/312) A:2 (45/191) M:2 (39/620) = 45
>start {"formatid":"gen6doublescustomgame","seed":[39873,38134,5348,40490]}
>player p1 {"name":"Bot 1","team":[{"name":"Delphox","species":"Delphox","gender":"","item":"ganlonberry","ability":"compoundeyes","moves":["belch","pound","shiftgear","disable"],"evs":{"hp":40,"atk":129,"def":157,"spa":62,"spd":64,"spe":6},"ivs":{"hp":15,"atk":21,"def":4,"spa":1,"spd":8,"spe":23},"nature":"bashful","level":61,"happiness":179,"shiny":false},{"name":"Ralts","species":"Ralts","gender":"","item":"icyrock","ability":"magician","moves":["waterfall","gigaimpact","curse","mistyterrain"],"evs":{"hp":164,"atk":46,"def":59,"spa":251,"spd":160,"spe":169},"ivs":{"hp":12,"atk":7,"def":13,"spa":1,"spd":10,"spe":1},"nature":"lax","level":68,"happiness":181,"shiny":false},{"name":"Haxorus","species":"Haxorus","gender":"","item":"weaknesspolicy","ability":"grasspelt","moves":["icywind","blueflare","swordsdance","pluck"],"evs":{"hp":167,"atk":113,"def":215,"spa":197,"spd":148,"spe":210},"ivs":{"hp":17,"atk":30,"def":26,"spa":17,"spd":10,"spe":26},"nature":"naughty","level":62,"happiness":0,"shiny":false},{"name":"Clefable","species":"Clefable","gender":"","item":"babiriberry","ability":"parentalbond","moves":["hornleech","toxic","gyroball","watersport"],"evs":{"hp":118,"atk":83,"def":246,"spa":247,"spd":231,"spe":240},"ivs":{"hp":20,"atk":23,"def":16,"spa":21,"spd":2,"spe":2},"nature":"relaxed","level":69,"happiness":74,"shiny":false},{"name":"Pidove","species":"Pidove","gender":"","item":"diancite","ability":"rockhead","moves":["growl","mudshot","lick","eggbomb"],"evs":{"hp":96,"atk":141,"def":245,"spa":123,"spd":203,"spe":114},"ivs":{"hp":24,"atk":26,"def":9,"spa":24,"spd":26,"spe":22},"nature":"timid","level":83,"happiness":44,"shiny":false},{"name":"Garchomp","species":"Garchomp-Mega","gender":"","item":"waterstone","ability":"stickyhold","moves":["acupressure","watergun","dragondance","imprison"],"evs":{"hp":177,"atk":210,"def":143,"spa":88,"spd":84,"spe":175},"ivs":{"hp":21,"atk":15,"def":18,"spa":12,"spd":25,"spe":5},"nature":"adamant","level":64,"happiness":85,"shiny":false}]}
>player p2 {"name":"Bot 2","team":[{"name":"Jolteon","species":"Jolteon","gender":"","item":"belueberry","ability":"voltabsorb","moves":["transform","sketch","crabhammer","sleeptalk"],"evs":{"hp":165,"atk":142,"def":89,"spa":18,"spd":127,"spe":17},"ivs":{"hp":2,"atk":8,"def":29,"spa":0,"spd":5,"spe":1},"nature":"mild","level":55,"happiness":3,"shiny":false},{"name":"Barboach","species":"Barboach","gender":"","item":"ringtarget","ability":"lightmetal","moves":["hornattack","inferno","aircutter","leechlife"],"evs":{"hp":77,"atk":85,"def":29,"spa":170,"spd":39,"spe":203},"ivs":{"hp":16,"atk":13,"def":30,"spa":5,"spd":1,"spe":27},"nature":"lonely","level":55,"happiness":53,"shiny":false},{"name":"Golem","species":"Golem","gender":"","item":"steelgem","ability":"poisonheal","moves":["leafstorm","foulplay","bulkup","endeavor"],"evs":{"hp":134,"atk":74,"def":98,"spa":65,"spd":7,"spe":37},"ivs":{"hp":9,"atk":13,"def":9,"spa":6,"spd":24,"spe":12},"nature":"docile","level":91,"happiness":133,"shiny":false},{"name":"Binacle","species":"Binacle","gender":"","item":"heavyball","ability":"sheerforce","moves":["powerswap","softboiled","naturepower","irontail"],"evs":{"hp":208,"atk":3,"def":49,"spa":72,"spd":187,"spe":50},"ivs":{"hp":19,"atk":22,"def":1,"spa":16,"spd":21,"spe":13},"nature":"quiet","level":55,"happiness":204,"shiny":false},{"name":"Dusclops","species":"Dusclops","gender":"","item":"skullfossil","ability":"flamebody","moves":["razorwind","doubleslap","conversion","magmastorm"],"evs":{"hp":44,"atk":129,"def":69,"spa":148,"spd":185,"spe":127},"ivs":{"hp":2,"atk":29,"def":14,"spa":24,"spd":8,"spe":26},"nature":"quirky","level":56,"happiness":5,"shiny":false},{"name":"Zigzagoon","species":"Zigzagoon","gender":"","item":"aerodactylite","ability":"battlearmor","moves":["smellingsalts","fireblast","strength","switcheroo"],"evs":{"hp":78,"atk":29,"def":2,"spa":243,"spd":128,"spe":230},"ivs":{"hp":17,"atk":9,"def":20,"spa":7,"spd":21,"spe":31},"nature":"relaxed","level":55,"happiness":174,"shiny":false}]}
>p1 team 1
>p2 team 1
>p1 switch 3, move 1 2
>p2 move 1 2, move 1 1
>p1 move 1, move 2 1
>p2 move 3, move 2 2
>p1 move 2 1, move 1
>p2 switch 3, move 3
>p1 move 3, move 4
>p2 move 1 2, switch 4
>p1 pass, switch 4
>p1 move 4 2, move 1 1
>p2 move 2 2, move 1 1
>p1 switch 5, move 2 1
>p2 move 3, move 2
>p2 switch 4, pass
>p1 move 1, move 3 2
>p2 move 4 2, move 3 2
>p1 move 2 2, move 4
>p2 switch 5, move 2
>p1 move 3 2, move 2 1
>p2 move 1, move 4 2
>p1 move 4 2, move 3 2
>p2 move 1, switch 6
>p2 pass, switch 5
>p1 switch 6, move 3 1
>p2 move 4 2, move 2 1
>p1 move 1 -1, move 1 1
>p2 move 3, move 4 2
>p1 move 2 2, move 4
>p2 move 1, move 2 1
>p2 switch 3, pass
>p1 move 3, move 3 2
>p2 move 2 1, move 3
>p1 move 4, move 1 1
>p2 switch 6, move 4 1
>p2 switch 6, pass
>p1 move 3, move 2 1
>p2 move 3 1, move 1 2
>p1 move 4, move 3 2
>p2 move 4, move 3
>p1 pass, switch 6
>p1 move 4, switch 5
>p2 move 2, pass
>p2 move 3 2, pass

(FWIW: I can add logic to the AI to ignore all choice errors after a |callback to work around this, but I don't think that's the correct approach)

Slayer95 commented 5 years ago

so sends '>p2 move 3 2, pass', only to get back 2 choice errors:

The errors correspond to the bad command >p2 move 2, pass. Actually, it should have aborted on the invalid move 2.

This is another regression from 698fb2a.

Slayer95 commented 5 years ago

Actually, it should have aborted on the invalid move 2.

This is required because crafting an invalid first choice would allow the player to figure out which decisions of the last Pokémon are valid or not.