Advertisement
Guest User

AoC2018 Day 15 [ Not working ]

a guest
Dec 15th, 2018
222
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.18 KB | None | 0 0
  1. #!/usr/bin/python3
  2.  
  3. from enum import Enum, unique
  4. from queue import Queue
  5. from random import choice
  6. from copy import deepcopy
  7. from glob import glob
  8.  
  9. def printMap(map):
  10.     print("".join(["".join(row) for row in map]))
  11.  
  12. @unique
  13. class T(Enum):
  14.     G = 1,
  15.     E = 2
  16.     def __repr__(self):
  17.         return self.name    
  18.  
  19. class Unit:
  20.     def __init__(self, type, row, col):
  21.         self.type = type
  22.         self.row = row
  23.         self.col = col
  24.         self.hp = 200
  25.         self.ap = 3
  26.     def __repr__(self):
  27.         return "[{}]-{}-({},{})".format(repr(self.type), self.hp, self.row, self.col)
  28.     def __eq__(self, other):
  29.         return self.type == other.type and \
  30.             self.row == other.row and \
  31.             self.col == other.col and \
  32.             self.hp == other.hp
  33.     def getSP(self, m):
  34.         rps = []
  35.         for r, c in [(-1, 0), (0, -1), (0, 1), (1, 0)]:
  36.             rr = self.row + r
  37.             rc = self.col + c
  38.             if rr >= 0 and rr < m.maxRow() and \
  39.                 rc >= 0 and rc < m.maxCol() and \
  40.                 m.getC(rr, rc) == ".":
  41.                     rps.append((rr, rc))
  42.         return rps
  43.     def distanceTo(self, p):
  44.         return abs(p[0] - self.row) + abs(p[1] - self.col)    
  45.  
  46. class Node:
  47.     def __init__(self, parent=None, position=None, depth=None):
  48.         self.parent = parent
  49.         self.position = position
  50.         self.depth = depth
  51.         self.isFinal = False
  52.     def __eq__(self, other):
  53.         return self.position == other.position
  54.     def __repr__(self):
  55.         #return "N:" + str(self.position) + "-P:" + str(self.parent)
  56.         return "N:" + str(self.position)
  57.  
  58. class Map:
  59.     def __init__(self):
  60.         self.c = []
  61.     def loadFromFile(self, fn):
  62.         with open(fn, "r") as f:
  63.             for line in f:
  64.                 self.addRow(list(line.strip()))
  65.     def addRow(self, row):
  66.         self.c.append(row)
  67.     def __repr__(self):
  68.         return "\n".join(["".join(row) for row in self.c])
  69.     def pPrint(self):
  70.         print(repr(self))
  71.     def maxCol(self):
  72.         return len(self.c[0])
  73.     def maxRow(self):
  74.         return len(self.c)
  75.     def getC(self, row, col):
  76.         return self.c[row][col]
  77.     def setC(self, row, col, val):
  78.         self.c[row][col] = val
  79.     def getUnits(self):
  80.         units = []
  81.         for row in range(self.maxRow()):
  82.             for col in range(self.maxCol()):                
  83.                 if self.getC(row, col) == "E":
  84.                     units.append(Unit(T.E, row, col))
  85.                 elif self.getC(row, col) == "G":
  86.                     units.append(Unit(T.G, row, col))
  87.         return units
  88.     def getInRange(self, unit, units):
  89.         r = set()
  90.         for u in units:
  91.             if u != unit and u.type != unit.type and u.hp > 0:
  92.                 r.update(u.getSP(self))
  93.         return r
  94.     def getPath(self, unit, goal):
  95.         startNode = Node(None, (unit.row, unit.col), 0)        
  96.         endNode = Node(None, (goal[0], goal[1]))
  97.         #print("SN:", startNode, "EN:", endNode)
  98.         nodesLeft = [startNode]
  99.         visitedNodes = []
  100.         maxDepth = None
  101.         while len(nodesLeft) > 0:
  102.             #print("LNL:", len(nodesLeft), "MD:", maxDepth)
  103.             currentNode = nodesLeft.pop(0)
  104.             if currentNode == endNode:
  105.                 if maxDepth is None:
  106.                     maxDepth = currentNode.depth
  107.                 currentNode.isFinal = True
  108.             else:
  109.                 for r, c in [(-1, 0), (0, -1), (0, 1), (1, 0)]:
  110.                     rr = currentNode.position[0] + r
  111.                     cc = currentNode.position[1] + c
  112.                     if rr >= 0 and rr < self.maxRow() and cc >= 0 and cc < self.maxCol():
  113.                         if self.getC(rr, cc) == ".":
  114.                             if maxDepth is not None:
  115.                                 if currentNode.depth + 1 <= maxDepth:
  116.                                     if currentNode not in visitedNodes:
  117.                                         nodesLeft.append(Node(currentNode, (rr, cc), currentNode.depth + 1))
  118.                             else:
  119.                                 if currentNode not in visitedNodes:
  120.                                     nodesLeft.append(Node(currentNode, (rr, cc), currentNode.depth + 1))
  121.             visitedNodes.append(currentNode)
  122.         paths = []
  123.         fnodes = [node for node in visitedNodes if node.isFinal]
  124.         if len(fnodes) == 0:
  125.             return None
  126.         #print("FNODES:", fnodes)
  127.         for node in fnodes:
  128.             currentPath = []
  129.             while node is not None:
  130.                 currentPath.append(node.position)
  131.                 node = node.parent
  132.             #print(currentPath[::1])
  133.             paths.append(currentPath[::-1])
  134.         #print("PATHS:", paths)
  135.         paths = sorted(paths, key = lambda x: x[1][1])
  136.         paths = sorted(paths, key = lambda x: x[1][0])
  137.         #print("SELECTED PATH:", paths[0])
  138.         return paths[0]
  139.     def getReachable(self, unit, units):
  140.         ps = self.getInRange(unit, units)
  141.         rp = []
  142.         for p in ps:
  143.             path = self.getPath(unit, p)
  144.             #print("P:", p, "PATH:", path)
  145.             if path is not None:
  146.                 rp.append((p, len(path)))
  147.         return rp
  148.     def getNearestPoints(self, unit, units):
  149.         rp = self.getReachable(unit, units)
  150.         #print("RP:", rp)
  151.         if len(rp) == 0:
  152.             return []
  153.         minDist = min([x[1] for x in rp])
  154.         r = [x[0] for x in rp if x[1] == minDist]
  155.         #print(r)
  156.         return r
  157.     def getNextReadingOrderPoint(self, points):
  158.         minRow = min([p[0] for p in points])
  159.         minRowPoints = [p for p in points if p[0] == minRow]
  160.         minCol = min([p[1] for p in minRowPoints])
  161.         return (minRow, minCol)
  162.     def distanceP2P(self, p1, p2):
  163.         return abs(p2[0] - p1[0]) + abs(p2[1] - p1[1])
  164.     def getNextPosition(self, unit, units):
  165.         enemyPositions = []
  166.         for u in units:
  167.             if unit.type != u.type and unit.hp > 0:
  168.                 enemyPositions.append((u.row, u.col))
  169.         # Don't move if enemy in range
  170.         for p in enemyPositions:
  171.             if self.distanceP2P((unit.row, unit.col), p) == 1:
  172.                 return None
  173.         points = self.getNearestPoints(unit, units)
  174.         if len(points) == 0:
  175.             return None
  176.         nextGoal = self.getNextReadingOrderPoint(points)
  177.         path = self.getPath(unit, nextGoal)
  178.         #print(path)
  179.         if path is None:
  180.             return None
  181.         else:
  182.             if len(path) < 2:
  183.                 return None
  184.             return path[1]
  185.     def moveUnit(self, unit, p):
  186.         self.setC(unit.row, unit.col, ".")
  187.         unit.row, unit.col = p[0], p[1]
  188.         self.setC(unit.row, unit.col, repr(unit.type))
  189.     def adjacentEnemies(self, unit, units):
  190.         chosenEnemies = []        
  191.         for r, c in [(-1, 0), (0, -1), (0, 1), (1, 0)]:
  192.             rr = unit.row + r
  193.             rc = unit.col + c
  194.             for u in units:
  195.                 if u.row == rr and u.col == rc and u.type != unit.type and u.hp > 0:
  196.                     chosenEnemies.append(u)
  197.                     break
  198.         return chosenEnemies
  199.     def getEnemyToAttack(self, unit, units):
  200.         possibleEnemies = self.adjacentEnemies(unit, units)
  201.         if len(possibleEnemies) == 0:
  202.             return None
  203.         maxHP = 1000
  204.         enemies = []
  205.         for enemy in possibleEnemies:
  206.             if enemy.hp < maxHP:
  207.                 enemies = [enemy]
  208.                 maxHP = enemy.hp
  209.             elif enemy.hp == maxHP:
  210.                 enemies.append(enemy)
  211.         ed = {}
  212.         for enemy in enemies:
  213.             ed[(enemy.row, enemy.col)] = enemy
  214.         k = self.getNextReadingOrderPoint(ed.keys())
  215.         return ed[k]
  216.     def attack(self, unit, units):        
  217.         enemy = self.getEnemyToAttack(unit, units)
  218.         if enemy is not None:
  219.             if unit.hp > 0 and enemy.hp > 0:
  220.                 enemy.hp = enemy.hp - unit.ap
  221.                 if enemy.hp <= 0:
  222.                     return True
  223.         return False
  224.     def removeUnit(self, unit, units):
  225.         #units.remove(unit)
  226.         self.setC(unit.row, unit.col, ".")
  227.     def onlyOneGroupLeft(self, units):
  228.         elves = [x for x in units if x.type == T.E and x.hp > 0]
  229.         goblins = [x for x in units if x.type == T.G and x.hp > 0]
  230.         return len(elves) == 0 or len(goblins) == 0
  231.     def reorderUnits(self, units):
  232.         d = {}
  233.         for unit in units:
  234.             d[(unit.row, unit.col)] = unit
  235.         lk = list(d.keys())
  236.         lk = sorted(lk, key=lambda x: x[1])
  237.         lk = sorted(lk, key=lambda x: x[0])
  238.         newUnits = []
  239.         for k in lk:
  240.             newUnits.append(d[k])
  241.         return newUnits
  242.  
  243. def runSimulation(filename):
  244.     m = Map()
  245.     m.loadFromFile(filename)
  246.     units = m.getUnits()
  247.     i = 0
  248.     while True:
  249.         print("ROUND:", i)
  250.         roundComplete = True      
  251.         end = False                  
  252.         for unit in units:
  253.             print("Unit:", unit)                        
  254.             if unit.hp > 0:
  255.                 if m.onlyOneGroupLeft(units):
  256.                     end = True
  257.                     break                                  
  258.                 p = m.getNextPosition(unit, [u for u in units if u.hp > 0])
  259.                 if p is not None:
  260.                     m.moveUnit(unit, p)                
  261.                 m.attack(unit, [u for u in units if u.hp > 0])
  262.                 if m.onlyOneGroupLeft(units):
  263.                     roundComplete = False                        
  264.         for unit in units:
  265.             if unit.hp <= 0:
  266.                 m.removeUnit(unit, units)
  267.         units = m.reorderUnits([u for u in units if u.hp > 0])
  268.         if roundComplete:
  269.             i = i + 1
  270.         if end:
  271.             totalHP = sum([x.hp for x in units if x.hp > 0])            
  272.             print("Name:", filename, "Rounds:", i, "Total HP:", totalHP, "Solution:", totalHP * i)
  273.             print("== Final State ==")
  274.             m.pPrint()
  275.             return
  276.  
  277. # l = glob("test_*.txt")
  278. # for filename in l:
  279. #     runSimulation(filename)
  280. #     input()        
  281.  
  282. runSimulation("input.txt")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement