Advent of code

 22 décembre 2015 

  Wizard Simulator 20XX :
  1. code.py
from pprint import pprint
from functools import cache
import itertools, math

SPELLS   = tuple(map(lambda t:dict(zip(['name',        'cost', 'turns', 'damage', 'armor', 'heal', 'mana'], t)), [ 
                                       ['Magic Missile', 53,      0,        4,       0,      0,      0],
                                       ['Drain',         73,      0,        2,       0,      2,      0],
                                       ['Shield',       113,      6,        0,       7,      0,      0],
                                       ['Poison',       173,      6,        3,       0,      0,      0],
                                       ['Recharge',     229,      5,        0,       0,      0,     101],
    ]))

BOSS_POINTS = 71
BOSS_DAMAGE = 10
MY_POINTS = 50
MY_MANA = 500


SPELLS_TURNS = len(SPELLS) * (0,)

FAIL = -1

def applySpells(myPoints, myMana, bossPoints, spellsTurns):
    myArmor = 0
    for spellIndex in range(len(SPELLS)):
        if spellsTurns[spellIndex] > 0:
            spellsTurns[spellIndex] -= 1
            spell = SPELLS[spellIndex]
            bossPoints -= spell['damage']
            myArmor    += spell['armor']
            myPoints   += spell['heal']
            myMana     += spell['mana']
    return (myPoints, myMana, bossPoints, myArmor)

# Trop long sans memoizing !
@cache
def getMinManaToWin(myPoints, myMana, bossPoints, spellsTurns, partie2 = False):
    saveSpellsTurns = list(spellsTurns)


    if (partie2):
        myPoints -= 1
        if myPoints <= 0:
            return (FAIL, None)

    # Spells at Player turn
    (saveMyPoints, saveMyMana, saveBossPoints, saveMyArmor) = applySpells(myPoints, myMana, bossPoints, saveSpellsTurns)    

    minCost = None
    minSpells = None
    for spellIndex in range(len(SPELLS)):
        spell = SPELLS[spellIndex]
        if spell['cost'] > saveMyMana:
            continue
        if saveSpellsTurns[spellIndex] > 0:
            continue

        # reinit valeurs
        (myPoints, myMana, bossPoints, spellsTurns) = (saveMyPoints, saveMyMana, saveBossPoints, saveSpellsTurns[:])

        myMana -= spell['cost']

        if spell['turns'] == 0:
            bossPoints -= spell['damage']
            myPoints += spell['heal']
            myMana += spell['mana']
        else:
            spellsTurns[spellIndex] += spell['turns']

        # Spells at Boss turn
        (myPoints, myMana, bossPoints, myArmor) = applySpells(myPoints, myMana, bossPoints, spellsTurns)

        if bossPoints <= 0:
            theCost = 0
            theSpells = []
        else:
            myPoints -= (max(1, BOSS_DAMAGE - myArmor))
            if myPoints <= 0:
                continue
            (theCost, theSpells) = getMinManaToWin(myPoints, myMana, bossPoints, tuple(spellsTurns), partie2)
            if theCost == FAIL:
                continue
        theCost += spell['cost']
        if minCost == None or theCost < minCost:
            minCost = theCost
            minSpells = [spell['name']] + theSpells

    if minCost == None:
        return (FAIL, None)
    else:
        return (minCost, minSpells)



print(getMinManaToWin(MY_POINTS, MY_MANA, BOSS_POINTS, SPELLS_TURNS, partie2=False))
print(getMinManaToWin(MY_POINTS, MY_MANA, BOSS_POINTS, SPELLS_TURNS, partie2=True))