def neighbour_locs(self, loc): """Return the list of neighbour locations of `tile`.""" x, y = loc coords = (-1, 0, 1) locs = set( (x + n, y + m) for n in coords for m in coords) - set([(x, y)]) return [Loc(*tup) for tup in locs if self.valid(Loc(*tup))]
def find_shortest(self, origin, target, extra_vertices=None): """Find shortest path using given vertices and static level vertices.""" nodes = defaultdict(dict) origin = getattr(origin, "loc", origin) target = getattr(target, "loc", target) # make a surrounding box (larger by 3 tiles in each direction than origin/target locations) as an # optimization to make dijkstra algorithm faster minx, maxx = min(origin.x, target.x), max(origin.x, target.x) miny, maxy = min(origin.y, target.y), max(origin.y, target.y) minx, maxx = max(0, minx - 3), min(xmax, maxx + 3) miny, maxy = max(0, miny - 3), min(ymax, maxy + 3) p1, p2 = Loc(minx, miny), Loc(maxx, maxy) # for dijkstra path alg, consider origin and destination as passable passable = lambda _loc: _loc in (origin, target) or not self.blocked( _loc) locs = [l for l in self if self.in_box((p1, p2), l)] locs = filter(passable, locs) for loc in locs: nlst = filter(passable, self.neighbour_locs(loc)) for nloc in nlst: dist = 1 if (nloc.x == loc.x or nloc.y == loc.y) else 1.5 nodes[loc][nloc] = dist # log("nodes", pformat(dict(nodes))) # find shortest path # log("origin", origin) # log("target", target) try: shortest = dijkstra.shortestPath(nodes, origin, target) return shortest[1:] except KeyError: # path is blocked completely return [] return vertices = field.vertices + [origin, loc] + extra_vertices # make nodes dictionary for dijkstra function; dict is of format # {vertice1: {vertice2: distance}} for vertice in vertices: for vert2 in vertices: if vertice != vert2: path = self.path(vertice, vert2) if not self.blocked(path): distance = self.distance(vertice, vert2) if vertice in nodes: nodes[vertice][vert2] = distance else: nodes[vertice] = {vert2: distance} if vert2 in nodes: nodes[vert2][vertice] = distance else: nodes[vert2] = {vertice: distance}
def make_win_lines(self): lines, diag1, diag2 = [], [], [] for n in range(3): lines.append([Loc(m, n) for m in range(3)]) lines.append([Loc(n, m) for m in range(3)]) diag1.append(Loc(n, n)) diag2.append(Loc(2 - n, n)) lines.extend((diag1, diag2)) self.win_lines = lines
def make_win_lines(self): """Create a list of winning lines -- when a player fills any one of them, he wins.""" winlines, diag1, diag2 = [], [], [] for n in range(size): winlines.append([Loc(m, n) for m in range(size)]) winlines.append([Loc(n, m) for m in range(size)]) diag1.append(Loc(n, n)) diag2.append(Loc(size - n - 1, n)) return winlines + [diag1, diag2]
def __init__(self, size, def_tile, puzzle): super(SudokuBoard, self).__init__(size, def_tile) for tile, val in zip(self, puzzle): if val != blank: self[tile] = Initial(val) self.regions = [self.make_region(xo, yo) for xo in offsets for yo in offsets] lines = [] for n in rng9: lines.extend(( [Loc(x, n) for x in rng9], [Loc(n, y) for y in rng9] )) self.lines = lines
def init_level(self): field.load_map("empty") level.populate() # make hero's party level.hero = Being('party', field.random(), level.last_index) level.last_index += 1 level.hero.place() level.hoplite = Being("hoplite", Loc(1, 1), level.last_index) level.last_index += 1 level.fencer = Being("fencer", Loc(1, 2), level.last_index) level.last_index += 1 level.mage = Being("mage", Loc(1, 3), level.last_index) level.last_index += 1 field.full_display([level.hero])
def attack_hero(self, target=None): """Attack hero by autopilot.""" if self.hostile: target = target or level.hero self.attack_hero_flag = True path = self.fullpath(target.loc) loc = first(path) if loc: self.move_to(Loc(*loc))
def __init__(self, *args, **kwargs): num_mines = kwargs.pop("num_mines") super(MinesBoard, self).__init__(*args, **kwargs) self.divider = '-' * (self.width * 4 + 4) self.current = Loc(0,0) self.hl_visible = False for _ in range(num_mines): self.random_empty().mine = True for tile in self: tile.number = sum( nbtile.mine for nbtile in self.neighbours(tile) )
def test_special_attacks(self): level_num = 1 field.load_map("local") level.hero = Being('party', field.random(), level.last_index) level.last_index += 1 # level.hero.place() level.hoplite = Being("hoplite", Loc(36, 10), level.last_index) level.hoplite.place() level.last_index += 1 level.fencer = Being("fencer", Loc(35, 10), level.last_index) level.fencer.place() level.last_index += 1 level.mage = Being("mage", Loc(35, 11), level.last_index) level.mage.place() level.last_index += 1 t = Being("troll", Loc(34, 9), level.last_index) t.team = "monsters" t.place() level.monsters = [t] level.last_index += 1 conf.mode = "tactical"
def burrow_points(self, loc, dirs, x_fin, y_fin): """ Return borrow points, used to tell if connecting corridor will interfere with other rooms or corridors. loc : start location dirs : tuple of directions where a dir is e.g. 0,1 for 'right' x_fin : terminate x dir at `x_fin` (similarly for `y_fin`) """ start = loc points = [] for dir in dirs: if dir[0] in (1, -1): x, y = loc step = dir[0] lst = range(x, x_fin + step, step) points.extend([Loc(x, y) for x in lst]) elif dir[1] in (1, -1): x, y = loc step = dir[1] lst = range(y, y_fin + step, step) points.extend([Loc(x, y) for y in lst]) return points
def parse_fmt(self, inp, fmt): """Attempt to parse `inp` using `fmt` format; return False if there is mismatch.""" from board import Loc fmt = fmt.split() inp = copy(inp) commands = [] handlers = {"%d": int, "%f": float, "%s": str} regexes = dict(self.regexes) def nomatch(val): return bool(optional and not re.match(regex, val)) for n, code in enumerate(fmt): optional = code.endswith('?') if optional: code = code[:-1] regex = "^%s$" % regexes.get(code, code) if not inp: if optional: continue else: raise ValueError if code == "loc": if nomatch(ujoin(inp[:2])): continue else: # print("inp", inp) x, y = inp.pop(0), inp.pop(0) loc = Loc(int(x) - 1, int(y) - 1) if self.board and not self.board.valid(loc): raise IndexError commands.append(loc) elif code == "%hd": # 'human' format, 1-indexed, integer if nomatch(first(inp)): continue else: commands.append(int(inp.pop(0)) - 1) else: if nomatch(first(inp)): continue else: commands.append(handlers.get(code, str)(inp.pop(0))) if inp: raise ValueError return commands
def get_loc(self, origin, moves): """ Get a location by performing a series of moves from origin. This is used by being.special_attack() to find out if a special attack is valid for current locations of party and monsters. Example: get_loc((5,5), (("right", 1), ("up", 1))) => (6,6) Note that only "right", "up", "down", "left" directions are possible. """ x, y = origin for dir, dist in moves: if dir == "up": # note that 0 is top of screen and then it increases as it goes down y -= dist elif dir == "right": x += dist elif dir == "down": y += dist elif dir == "left": x -= dist return Loc(x, y)
def turn(self): """Get player's move, return Location to move OR return `newgame_code` to start a new game.""" reversi.check_for_quit() # get button click or move click on a tile button = reversi.get_button_click(self.newgame, self.hints) if not button: pass # click outside of tiles / buttons elif button == self.newgame: board.reset() return newgame_code elif button == self.hints: board.toggle_hints(self.piece) else: move_to = board.get_clicked_tile(Loc(button)) if move_to and board.is_valid_move(self.piece, move_to): return move_to self.newgame, self.hints = board.buttons() reversi.mainclock_tick()
def path(self, origin, target): """ Build navigation path. Note: this is a 'dumb' path, it will go over walls and monsters. """ origin = getattr(origin, "loc", origin) target = getattr(target, "loc", target) x, y = target ox, oy = origin path = [] # for _ in range(100): while True: # log(path) if (ox, oy) == tuple(target): return path if x == ox: if y > oy: oy += 1 else: oy -= 1 elif y == oy: if x > ox: ox += 1 else: ox -= 1 elif x > ox: if y > oy: ox += 1 oy += 1 else: ox += 1 oy -= 1 elif x < ox: if y > oy: ox -= 1 oy += 1 else: ox -= 1 oy -= 1 path.append(Loc(ox, oy))
def get_coords(self, loc, direction): """ Returns coordinates of a cell adjacent to our cell in given direction. example: if we're at 1,1 and direction is l (right), return 1,2. """ direction = int(direction) (x, y) = loc if direction == 108: x += 1 # right if direction == 107: y -= 1 # down if direction == 104: x -= 1 # left if direction == 106: y += 1 # up if direction == 98: x -= 1 y += 1 # left + up if direction == 110: x += 1 y += 1 # right + up if direction == 121: x -= 1 y -= 1 # left + down if direction == 117: x += 1 y -= 1 # right + down return Loc(x, y)
def nlocs(self, loc): x, y = loc locs = ((x, y - 1), (x + 1, y), (x, y + 1), (x - 1, y)) locs = [Loc(*tup) for tup in locs] return [(loc if self.valid(loc) else None) for loc in locs]
def __init__(self, *a, **kw): super().__init__(*a, **kw) self.current = Loc(0,0)
def __init__(self, *a, **kw): super().__init__(*a, **kw) self.current = Loc(0, 0) self.hl_visible = False
def __init__(self): x, y = board.middle() Piece(Loc(x, y), player1.char) Piece(Loc(x + 1, y + 1), player1.char) Piece(Loc(x + 1, y), player2.char) Piece(Loc(x, y + 1), player2.char)
def make_region(self, xo, yo): """Make one region at x offset `xo` and y offset `yo`.""" return [ Loc(xo + x, yo + y) for x in rng3 for y in rng3 ]
def __iter__(self): for x in range(conf.xmax): for y in range(conf.ymax): yield Loc(x + 1, y + 1)
def center(self): return Loc(self.width // 2, self.height // 2)