Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python3
- from enum import Enum, unique
- from queue import Queue
- from random import choice
- from copy import deepcopy
- from glob import glob
- def printMap(map):
- print("".join(["".join(row) for row in map]))
- @unique
- class T(Enum):
- G = 1,
- E = 2
- def __repr__(self):
- return self.name
- class Unit:
- def __init__(self, type, row, col):
- self.type = type
- self.row = row
- self.col = col
- self.hp = 200
- self.ap = 3
- def __repr__(self):
- return "[{}]-{}-({},{})".format(repr(self.type), self.hp, self.row, self.col)
- def __eq__(self, other):
- return self.type == other.type and \
- self.row == other.row and \
- self.col == other.col and \
- self.hp == other.hp
- def getSP(self, m):
- rps = []
- for r, c in [(-1, 0), (0, -1), (0, 1), (1, 0)]:
- rr = self.row + r
- rc = self.col + c
- if rr >= 0 and rr < m.maxRow() and \
- rc >= 0 and rc < m.maxCol() and \
- m.getC(rr, rc) == ".":
- rps.append((rr, rc))
- return rps
- def distanceTo(self, p):
- return abs(p[0] - self.row) + abs(p[1] - self.col)
- class Node:
- def __init__(self, parent=None, position=None, depth=None):
- self.parent = parent
- self.position = position
- self.depth = depth
- self.isFinal = False
- def __eq__(self, other):
- return self.position == other.position
- def __repr__(self):
- #return "N:" + str(self.position) + "-P:" + str(self.parent)
- return "N:" + str(self.position)
- class Map:
- def __init__(self):
- self.c = []
- def loadFromFile(self, fn):
- with open(fn, "r") as f:
- for line in f:
- self.addRow(list(line.strip()))
- def addRow(self, row):
- self.c.append(row)
- def __repr__(self):
- return "\n".join(["".join(row) for row in self.c])
- def pPrint(self):
- print(repr(self))
- def maxCol(self):
- return len(self.c[0])
- def maxRow(self):
- return len(self.c)
- def getC(self, row, col):
- return self.c[row][col]
- def setC(self, row, col, val):
- self.c[row][col] = val
- def getUnits(self):
- units = []
- for row in range(self.maxRow()):
- for col in range(self.maxCol()):
- if self.getC(row, col) == "E":
- units.append(Unit(T.E, row, col))
- elif self.getC(row, col) == "G":
- units.append(Unit(T.G, row, col))
- return units
- def getInRange(self, unit, units):
- r = set()
- for u in units:
- if u != unit and u.type != unit.type and u.hp > 0:
- r.update(u.getSP(self))
- return r
- def getPath(self, unit, goal):
- startNode = Node(None, (unit.row, unit.col), 0)
- endNode = Node(None, (goal[0], goal[1]))
- #print("SN:", startNode, "EN:", endNode)
- nodesLeft = [startNode]
- visitedNodes = []
- maxDepth = None
- while len(nodesLeft) > 0:
- #print("LNL:", len(nodesLeft), "MD:", maxDepth)
- currentNode = nodesLeft.pop(0)
- if currentNode == endNode:
- if maxDepth is None:
- maxDepth = currentNode.depth
- currentNode.isFinal = True
- else:
- for r, c in [(-1, 0), (0, -1), (0, 1), (1, 0)]:
- rr = currentNode.position[0] + r
- cc = currentNode.position[1] + c
- if rr >= 0 and rr < self.maxRow() and cc >= 0 and cc < self.maxCol():
- if self.getC(rr, cc) == ".":
- if maxDepth is not None:
- if currentNode.depth + 1 <= maxDepth:
- if currentNode not in visitedNodes:
- nodesLeft.append(Node(currentNode, (rr, cc), currentNode.depth + 1))
- else:
- if currentNode not in visitedNodes:
- nodesLeft.append(Node(currentNode, (rr, cc), currentNode.depth + 1))
- visitedNodes.append(currentNode)
- paths = []
- fnodes = [node for node in visitedNodes if node.isFinal]
- if len(fnodes) == 0:
- return None
- #print("FNODES:", fnodes)
- for node in fnodes:
- currentPath = []
- while node is not None:
- currentPath.append(node.position)
- node = node.parent
- #print(currentPath[::1])
- paths.append(currentPath[::-1])
- #print("PATHS:", paths)
- paths = sorted(paths, key = lambda x: x[1][1])
- paths = sorted(paths, key = lambda x: x[1][0])
- #print("SELECTED PATH:", paths[0])
- return paths[0]
- def getReachable(self, unit, units):
- ps = self.getInRange(unit, units)
- rp = []
- for p in ps:
- path = self.getPath(unit, p)
- #print("P:", p, "PATH:", path)
- if path is not None:
- rp.append((p, len(path)))
- return rp
- def getNearestPoints(self, unit, units):
- rp = self.getReachable(unit, units)
- #print("RP:", rp)
- if len(rp) == 0:
- return []
- minDist = min([x[1] for x in rp])
- r = [x[0] for x in rp if x[1] == minDist]
- #print(r)
- return r
- def getNextReadingOrderPoint(self, points):
- minRow = min([p[0] for p in points])
- minRowPoints = [p for p in points if p[0] == minRow]
- minCol = min([p[1] for p in minRowPoints])
- return (minRow, minCol)
- def distanceP2P(self, p1, p2):
- return abs(p2[0] - p1[0]) + abs(p2[1] - p1[1])
- def getNextPosition(self, unit, units):
- enemyPositions = []
- for u in units:
- if unit.type != u.type and unit.hp > 0:
- enemyPositions.append((u.row, u.col))
- # Don't move if enemy in range
- for p in enemyPositions:
- if self.distanceP2P((unit.row, unit.col), p) == 1:
- return None
- points = self.getNearestPoints(unit, units)
- if len(points) == 0:
- return None
- nextGoal = self.getNextReadingOrderPoint(points)
- path = self.getPath(unit, nextGoal)
- #print(path)
- if path is None:
- return None
- else:
- if len(path) < 2:
- return None
- return path[1]
- def moveUnit(self, unit, p):
- self.setC(unit.row, unit.col, ".")
- unit.row, unit.col = p[0], p[1]
- self.setC(unit.row, unit.col, repr(unit.type))
- def adjacentEnemies(self, unit, units):
- chosenEnemies = []
- for r, c in [(-1, 0), (0, -1), (0, 1), (1, 0)]:
- rr = unit.row + r
- rc = unit.col + c
- for u in units:
- if u.row == rr and u.col == rc and u.type != unit.type and u.hp > 0:
- chosenEnemies.append(u)
- break
- return chosenEnemies
- def getEnemyToAttack(self, unit, units):
- possibleEnemies = self.adjacentEnemies(unit, units)
- if len(possibleEnemies) == 0:
- return None
- maxHP = 1000
- enemies = []
- for enemy in possibleEnemies:
- if enemy.hp < maxHP:
- enemies = [enemy]
- maxHP = enemy.hp
- elif enemy.hp == maxHP:
- enemies.append(enemy)
- ed = {}
- for enemy in enemies:
- ed[(enemy.row, enemy.col)] = enemy
- k = self.getNextReadingOrderPoint(ed.keys())
- return ed[k]
- def attack(self, unit, units):
- enemy = self.getEnemyToAttack(unit, units)
- if enemy is not None:
- if unit.hp > 0 and enemy.hp > 0:
- enemy.hp = enemy.hp - unit.ap
- if enemy.hp <= 0:
- return True
- return False
- def removeUnit(self, unit, units):
- #units.remove(unit)
- self.setC(unit.row, unit.col, ".")
- def onlyOneGroupLeft(self, units):
- elves = [x for x in units if x.type == T.E and x.hp > 0]
- goblins = [x for x in units if x.type == T.G and x.hp > 0]
- return len(elves) == 0 or len(goblins) == 0
- def reorderUnits(self, units):
- d = {}
- for unit in units:
- d[(unit.row, unit.col)] = unit
- lk = list(d.keys())
- lk = sorted(lk, key=lambda x: x[1])
- lk = sorted(lk, key=lambda x: x[0])
- newUnits = []
- for k in lk:
- newUnits.append(d[k])
- return newUnits
- def runSimulation(filename):
- m = Map()
- m.loadFromFile(filename)
- units = m.getUnits()
- i = 0
- while True:
- print("ROUND:", i)
- roundComplete = True
- end = False
- for unit in units:
- print("Unit:", unit)
- if unit.hp > 0:
- if m.onlyOneGroupLeft(units):
- end = True
- break
- p = m.getNextPosition(unit, [u for u in units if u.hp > 0])
- if p is not None:
- m.moveUnit(unit, p)
- m.attack(unit, [u for u in units if u.hp > 0])
- if m.onlyOneGroupLeft(units):
- roundComplete = False
- for unit in units:
- if unit.hp <= 0:
- m.removeUnit(unit, units)
- units = m.reorderUnits([u for u in units if u.hp > 0])
- if roundComplete:
- i = i + 1
- if end:
- totalHP = sum([x.hp for x in units if x.hp > 0])
- print("Name:", filename, "Rounds:", i, "Total HP:", totalHP, "Solution:", totalHP * i)
- print("== Final State ==")
- m.pPrint()
- return
- # l = glob("test_*.txt")
- # for filename in l:
- # runSimulation(filename)
- # input()
- runSimulation("input.txt")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement