Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # -*- coding: utf-8 -*-
- #%%
- from collections import namedtuple,defaultdict
- from heapq import heappush,heappop
- from itertools import count
- Character=namedtuple('Character',['fraction','HP'])
- #%%
- START_HP=200
- ATTACK_DMG=3
- #%%
- def getData():
- chars=dict()
- field=set()
- with open("/home/thoma/Documents/playground/advent15.txt","r") as f:
- for m,l in enumerate(f.readlines()):
- l=l.strip('\n')
- for n,c in enumerate(l):
- if c!="#":
- node=(m,n)
- field.add(node)
- if c in 'GE':
- chars[(m,n)]=Character(c,START_HP)
- return field,chars
- #%%
- def getFracPos(frac,chars):
- return set(map(lambda x:x[0],filter(lambda x:x[1].fraction==frac,chars.items())))
- #%%
- DIRECTIONS=[(-1,0),(0,-1),(0,1),(1,0)]
- def getClosestEnemySpot(isGoblin,startPos,field,chars):
- enemies,allies='GE'
- if isGoblin:
- enemies,allies='EG'
- blocked=getFracPos(allies,chars)
- blocked.remove(startPos)
- enemyPos=getFracPos(enemies,chars)
- if len(enemyPos)==0:
- raise
- frontier=[]
- pred=dict()
- heappush(frontier,(0,startPos,startPos))
- while len(frontier):
- d,pos,oldPos=heappop(frontier)
- if pos in pred or pos in blocked:
- continue
- pred[pos]=oldPos
- m,n=pos
- for dm,dn in DIRECTIONS:
- newPos=m+dm,n+dn
- if newPos in enemyPos:
- while pred[pos]!=startPos:
- pos=pred[pos]
- return pos
- if newPos in field:
- heappush(frontier,(d+1,newPos,pos))
- #%%
- def attackEnemy(fraction,pos,chars,power=3):
- px,py=pos
- enemies='G' if fraction=='E' else 'E'
- attackPower=ATTACK_DMG if fraction=='G' else power
- enemiesAround=[]
- for dx,dy in DIRECTIONS:
- attackPos=(px+dx,py+dy)
- if attackPos in chars:
- if chars[attackPos].fraction != enemies:
- continue
- enemiesAround.append((chars[attackPos].HP,attackPos))
- if not len(enemiesAround):
- return
- _,attackPos=min(enemiesAround)
- newHP=chars[attackPos].HP-attackPower
- if newHP<=0:
- del(chars[attackPos])
- else:
- chars[attackPos]=Character(enemies,newHP)
- #%%
- def makeMove(pos,field,chars,power=3):
- ch=chars[pos]
- #print(ch)
- newPos=getClosestEnemySpot(ch.fraction=='G',pos,field,chars)
- if newPos:
- del(chars[pos])
- chars[newPos]=ch
- attackEnemy(ch.fraction,newPos,chars,power)
- ##%
- # for debugging
- #FX,FY=7,7
- #def printField(field,chars):
- # A=[["." for _ in range(FX)]for _ in range(FY)]
- # for pos,cha in chars.items():
- # px,py=pos
- # A[px][py]=cha.fraction
- # for a in A:
- # print("".join(a))
- #%%
- def turn(chars,field,power=3):
- for pos in sorted(chars):
- if pos not in chars:
- continue
- makeMove(pos,field,chars,power)
- #%%
- def simulate(elfAttack,part2=0):
- field,chars=getData()
- l=len(getFracPos('E',chars))
- for i in count():
- try:
- #print("--------")
- #print(i+1)
- turn(chars,field,elfAttack)
- if part2 and len(getFracPos('E',chars))<10:
- return -1
- #printField(field,chars)
- except:
- print(i*sum(map(lambda ch:ch.HP,chars.values())))
- break
- return 1
- #%%
- #part1
- simulate(3)
- #%%
- #part2
- for i in range(3,1000):
- if simulate(i,1)==1:
- break
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement