# helgefmi/ohno

Code shuffle/cleanup/rewriting for better code consistency

1 parent d5d0bc7 commit 9b47e750485202d1342266d800f91f01f432e379 committed Jun 10, 2011
6 .gitignore
 @@ -1,5 +1,5 @@ -lib -logs/* +/lib +/logs/* +/savegames *.pyc .*.swp -savegames
9 ohno/ai/pathing.py
 @@ -75,17 +75,12 @@ def _search(self): # Investigate each tile once if self.previous[neighbor.idx]: continue - # TODO: This is obviously a hack - if neighbor.in_shop: - continue # Don't search past two unexplored tiles in a direction if not current.explored and not neighbor.explored: continue - # TODO: Remove can_walk_diagonally and is_open_door and put the - # logic in here. # We can't move diagonally through an open door - can_diagonal = (current.can_walk_diagonally() and - neighbor.can_walk_diagonally()) + can_diagonal = (not current.is_open_door() and + not neighbor.is_open_door()) if (not can_diagonal and abs(neighbor.idx - current.idx) not in (1, 80)): continue
2 ohno/ai/strategy/explore.py
 @@ -22,7 +22,7 @@ def _closed_doors(self): self.ohno.logger.strategy('[explore] found closed door at %r' % tile) assert tile.walkable == False - assert tile.feature_is_a('Door') + assert tile.feature_isa('Door') if tile in self.ohno.dungeon.curtile.adjacent(): if tile.feature.locked:
16 ohno/appearance.py
 @@ -8,10 +8,6 @@ def __init__(self, glyph, color): self._fg = color['fg'] self._bold = color['bold'] - glyph = property(lambda self:self._glyph) - fg = property(lambda self:self._fg) - bold = property(lambda self:self._bold) - def __eq__(self, other): return hash(self) == hash(other) @@ -28,6 +24,18 @@ def __str__(self): def __hash__(self): return hash(str(self)) + @property + def glyph(self): + return self._glyph + + @property + def fg(self): + return self._fg + + @property + def bold(self): + return self._bold + STAIRCASE_DOWN = Appearance('>', {'fg': 37, 'bold': False}) STAIRCASE_UP = Appearance('<', {'fg': 37, 'bold': False}) OPEN_DOOR = Appearance('-', {'fg': 33, 'bold': False})
18 ohno/dungeon/dungeon.py
 @@ -10,16 +10,17 @@ def __init__(self, ohno): def update(self): """ - 1. Check if we're on a new level, and make on if we are - 2. Set self.curlevel and self.curtile, and update the current level + 1. Check if we're on a new level, and make on if we are. + 2. Set self.curlevel and self.curtile + 3. Update the current level. """ # TODO: Branches. We need to know which branch we are in and which # levels are in which branch. # This should probably be done inside this function, while # the detection code should be in Level. dlvl = self.ohno.hero.dlvl if dlvl not in self.levels: - self.ohno.logger.dungeon('Found new level (dlvl %d)!' % dlvl) + self.ohno.logger.dungeon('Found dlvl %d!' % dlvl) self.levels[dlvl] = Level(self.ohno, dlvl) newlevel = self.levels[dlvl] @@ -34,16 +35,19 @@ def update(self): MessageEvent.unsubscribe(self.curlevel.on_message) MessageEvent.subscribe(newlevel.on_message) self.curlevel = newlevel - newlevel.update() idx = self.ohno.hero.get_position_idx() self.curtile = self.curlevel.tiles[idx] - self.ohno.logger.dungeon('Current tile is %r' % self.curtile) + self.ohno.logger.dungeon( + 'curtile before updating level: %r' % self.curtile + ) + self.curlevel.update() + self.ohno.logger.dungeon( + 'curtile after updating level: %r' % self.curtile + ) # Some sanity checks assert self.curtile.idx == idx assert self.curtile.appearance == self.ohno.hero.appearance assert self.curtile.monster is None - - self.curlevel.farlook_monsters()
7 ohno/dungeon/item/item.py
 @@ -7,7 +7,6 @@ def __init__(self, ohno, appearance): self.ohno = ohno self.appearance = appearance - @staticmethod - def create(ohno, appearance): - """Checks `appearance` for which item to create""" - return Item(ohno, appearance) +def create(ohno, appearance): + """Checks `appearance` for which item to create""" + return Item(ohno, appearance)
78 ohno/dungeon/level.py
 @@ -17,61 +17,63 @@ class Level(object): def __init__(self, ohno, dlvl): self.ohno = ohno self.dlvl = dlvl + # Tiles should only mutate, never be replaced by new ones. self.tiles = tuple(Tile(self, x) for x in xrange(21 * 80)) self.monsters = [] self.max_searched = 12 def __str__(self): return '' % self.dlvl + __repr__ = __str__ - @queryable - def tiles_where(self): - return self.tiles + def _farlook_monsters(self): + # Check to see if there's a monster on this level that we're not sure + # what is yet. + for monster in self.monsters: + if not (len(monster.spoilers) > 1 or + (monster.peaceful is None and not monster.is_peaceful)): + continue + + self.ohno.logger.level('Doing farlook on %s (%s)' % ( + monster.tile, monster + )) + message = self.ohno.farlook(monster.tile) + self.ohno.logger.level('Got message: %s' % message) + info = Level.inspectname.search(message).groupdict() + self.ohno.logger.level('Parsed: %s' % info) + monster.monster_info(info) def update(self): + """ + This will update the current level. + 1. Make changes to the tiles of this level. + 2. Update newly explored tiles as explored. + 3. Farlook interesting monsters. + """ self.ohno.logger.level('Updating level..') maptiles = self.ohno.framebuffer.get_maptiles() - for (i, tile) in enumerate(self.tiles): + for i, tile in enumerate(self.tiles): maptile = maptiles[i] # No point in updating a tile that didn't change since last time. if tile.appearance != maptile: - #self.ohno.logger.level('Updating %d: %r' % (i, tile)) tile.set(maptile) - #self.ohno.logger.level('RESULT: %d: %r' % (i, tile)) - curtile = self.tiles[self.ohno.hero.get_position_idx()] - self.ohno.logger.level('Curtile after level update %r' % curtile) - curtile.explored = True + self.ohno.dungeon.curtile.explored = True # Since empty spaces might both be walkable and not, the only way to # find out (well.. at this point anyway) is to stand adjacent to the # square and see if it lights up (see if the glyph changes or not). # If it doesn't, we need to set the tile to not walkable. if not self.ohno.hero.blind: - for tile in curtile.adjacent(): + for tile in self.ohno.dungeon.curtile.adjacent(): if not tile.items: tile.explored = True if tile.appearance.glyph == ' ': tile._walkable = False - def farlook_monsters(self): - # Check to see if there's a monster on this level that we're not sure - # what is yet. - for monster in self.monsters: - do_farlook = (len(monster.spoilers) > 1 or - (monster.peaceful is None and - not monster.is_peaceful)) - - if do_farlook: - self.ohno.logger.level('Doing farlook on %s (%s)' % ( - monster.tile, monster - )) - message = self.ohno.farlook(monster.tile) - self.ohno.logger.level('Got message: %s' % message) - info = Level.inspectname.search(message).groupdict() - monster.monster_info(info) - + self._farlook_monsters() + def explored_progress(self): """ How much of the level do we think is explored? @@ -85,8 +87,8 @@ def explored_progress(self): # algorithms for normal levels and mine levels. # TODO: Normal levels should count rooms as well as check the amount of # walkable tiles. - num_walkable_tiles = sum( - 1 for x in self.tiles_where(walkable=True, explored=True) + num_walkable_tiles = len( + list(self.tiles_where(walkable=True, explored=True)) ) # Let's try 300 as the value of explored walkable tiles a level has on @@ -95,9 +97,11 @@ def explored_progress(self): def on_message(self, event): curtile = self.ohno.dungeon.curtile + if event.msgtype == 'locked_door': + assert self.ohno.last_action.isa('Open') tile = self.ohno.last_action.tile - assert tile.feature_is_a('Door') + assert tile.feature_isa('Door') self.ohno.logger.level('Locking %r@%s' % (tile, self)) tile.feature.lock() @@ -113,23 +117,21 @@ def on_message(self, event): self.ohno.logger.level('Setting %s to an open door' % curtile) curtile.set_feature(appearance.OPEN_DOOR) - #if event.msgtype == 'found_shop': - # shop_tiles = [tile for tile in curtile.adjacent(walkable=True) - # if tile.appearance != '#'] - # self.ohno.logger.level('shop_tiles: %s' % map(str, shop_tiles)) - # for tile in shop_tiles: - # tile.set_in_shop() - if event.msgtype == 'kicked_door': assert self.ohno.last_action.isa('Kick') + tile = self.ohno.last_action.tile self.ohno.logger.level( 'Setting %s to floor.' % self.ohno.last_action.tile ) - self.ohno.last_action.tile.set_feature(appearance.FLOOR) + tile.set_feature(appearance.FLOOR) if event.msgtype == 'opened_door': assert self.ohno.last_action.isa('Open') self.ohno.logger.level( 'Setting %s to floor.' % self.ohno.last_action.tile ) self.ohno.last_action.tile.set_feature(appearance.OPEN_DOOR) + + @queryable + def tiles_where(self): + return self.tiles
26 ohno/dungeon/monster.py
 @@ -6,22 +6,24 @@ class Monster(object): Represents a monster on a tile. """ - @staticmethod - def create(ohno, maptile): - """Checks `maptile` for which monster to create""" - return Monster(ohno, maptile) - def __init__(self, tile, maptile): self.ohno = tile.ohno self.tile = tile self.appearance = maptile + # Means we haven't explicitly checked yet. + self.peaceful = None self.spoilers = monsters.by_appearance[maptile] - if str(maptile) != 'I7': # Invisible monster + self.ohno.logger.monster('Found spoiler: %s' % self.spoilers) + + if str(maptile) not in ['I7', 'X7']: assert self.spoilers, maptile - self.ohno.logger.monster('Found spoiler: %s' % self.spoilers) - self.peaceful = None # Means we haven't explicitly checked yet. + def __str__(self): + return '' % ( + self.spoilers, self.peaceful + ) + __repr__ = __str__ def monster_info(self, info): self.peaceful = bool(info['peaceful']) @@ -43,8 +45,6 @@ def is_peaceful(self): return self.peaceful return all(x.is_peaceful() for x in self.spoilers) - def __str__(self): - return '' % ( - self.spoilers, self.peaceful - ) - __repr__ = __str__ +def create(ohno, maptile): + """Checks `maptile` for which monster to create""" + return Monster(ohno, maptile)
82 ohno/dungeon/tile.py
 @@ -1,11 +1,10 @@ import string -from ohno.dungeon.item.item import Item -from ohno.dungeon.monster import Monster +from queryable import queryable +from ohno.dungeon import monster from ohno.dungeon.feature import feature - -from queryable import queryable +from ohno.dungeon.item import item _tile_is_feature = lambda t: t.glyph in '.}{#_<>]^|-~ \\' or str(t) == 'm4' _tile_is_item = lambda t: t.glyph in '`0*\$[%)(/?!"=+' @@ -27,13 +26,24 @@ def __init__(self, level, idx): self.items = [] self.monster = None self.has_hero = None - self.in_shop = False self._adjacent = None self._orthogonal = None self._horizontal = None self._vertical = None + def __str__(self): + return '' % ( + self.idx, + self.appearance.glyph if self.appearance else ' ', + self.explored, + int(self.walkable or 0), + self.searched, + self.distance_from_hero() if self.ohno.ai.pathing.is_uptodate() + else -1 + ) + __repr__ = __str__ + def set(self, appearance): """ Will be called by `ohno.dungeon.level.update` if the current glyph or @@ -47,11 +57,12 @@ def set(self, appearance): self.explored = self.explored or appearance.glyph != ' ' # If it's the first time we're seeing the feature of this tile or if # it has changed (i.e. water can spread to nearby floortiles). - if (not self.feature) or self.feature.appearance != appearance: + if not self.feature or self.feature.appearance != appearance: self.set_feature(appearance) self.items = [] self.has_hero = False self.set_monster(None) + elif _tile_is_item(appearance): self.ohno.logger.tile('Itemtile! %r' % self) self._walkable = True @@ -65,26 +76,25 @@ def set(self, appearance): # If it stays the same, we'll simply assume nothing has changed. if not self.items or self.items[-1].appearance != appearance: self.ohno.logger.tile('New item!') - self.items = [Item.create(self, appearance)] + self.items = [item.create(self, appearance)] # Since this tile might have new information, # we set explored to False. That way, it'll be easier for our AI # to know this square is interesting. self.explored = False self.ohno.logger.tile('Itemtile is now %r' % self) - elif self.idx == self.ohno.hero.get_position_idx(): + + elif self == self.ohno.dungeon.curtile: self.ohno.logger.tile('Herotile! %r' % self) assert not self.has_hero self._walkable = True self.has_hero = True self.set_monster(None) if self.ohno.hero.appearance != appearance: - self.ohno.logger.tile('New hero! A was %s, now is %s' % ( - self.ohno.hero.appearance, appearance - )) # Not sure if we need this, but this seems like a good place to # check if we're polymorphed. self.ohno.hero.set_appearance(appearance) self.ohno.logger.tile('Herotile is now %r' % self) + elif _tile_is_monster(appearance): # TODO: Might not be true if this is a stone giant standing on a # boulder. @@ -93,16 +103,20 @@ def set(self, appearance): self._walkable = True if (not self.monster) or self.monster.appearance != appearance: self.ohno.logger.tile('New monster!') - self.set_monster(Monster.create(self, appearance)) + self.set_monster(appearance) self.ohno.logger.tile('Monstertile is now %r' % self) def set_feature(self, appearance): self.feature = feature.create(self, appearance) self._walkable = (self.is_open_door() or _tile_is_walkable(appearance)) - def set_monster(self, new_monster): + def set_monster(self, appearance): """Sets self.monster and updates Levle.monsters""" + new_monster = None + if appearance is not None: + new_monster = monster.create(self, appearance) + if new_monster == self.monster: return @@ -114,9 +128,6 @@ def set_monster(self, new_monster): self.monster = new_monster - def set_in_shop(self): - self.in_shop = True - @property def appearance(self): """What does this tile look like right now?""" @@ -141,6 +152,14 @@ def distance_from_hero(self): assert self.ohno.ai.pathing.tick == self.ohno.tick return self.ohno.ai.pathing.dists[self.idx] + def feature_isa(self, class_name): + return self.feature_name == class_name + + def is_open_door(self): + return (self.feature and + self.feature.appearance.glyph in '-|' and + self.feature.appearance.fg == 33) + # Methods for iterating neighbors @queryable def adjacent(self): @@ -211,49 +230,22 @@ def has_monster(self): @property def has_closed_door(self): - return self.feature_is_a('Door') and self.feature.closed + return self.feature_isa('Door') and self.feature.closed @property def feature_name(self): return self.feature.__class__.__name__ if self.feature else 'Unknown' @property def reachable(self): + assert self.ohno.ai.pathing.is_uptodate() return self.distance_from_hero() != float('inf') @property def walkable(self): - # TODO: The following is not true if we're currently a giant or have - # the ability to fly (i think :).. # This might be called before the tile is initialized, so make sure we # consider the case where appearance is None return (self.appearance and self._walkable and not self.monster and self.appearance.glyph != '0') - - def feature_is_a(self, class_name): - return self.feature_name == class_name - - def __str__(self): - return '' % ( - self.idx, self.appearance.glyph if self.appearance else ' ', - self.explored, int(self.walkable or 0), self.searched, - self.distance_from_hero() if self.ohno.ai.pathing.is_uptodate() - else -1 - ) - - def __repr__(self): - return str(self) - - # TODO: Meh, move this to the pathing code. - # I see no other uses for this function. - def is_open_door(self): - return (self.feature and - self.feature.appearance.glyph in '-|' and - self.feature.appearance.fg == 33) - - # TODO: Meh, move this to the pathing code. - # I see no other uses for this function. - def can_walk_diagonally(self): - return not self.is_open_door()
1 ohno/hero.py
 @@ -65,6 +65,7 @@ def __init__(self, ohno): def __str__(self): return '') + __repr__ = __str__ def get_position_idx(self): return self.position[0] * 80 + self.position[1]
3 ohno/ohno.py
 @@ -65,8 +65,9 @@ def loop(self): if not self.running: break - # Updates stats like hp, ac, hunger, score, dlvl + # Updates stats (bottomlines) and hero's position. self.hero.update() + # Creates new level and/or updates the level with what we got from # framebuffer. self.dungeon.update()
7 ohno/ui/debugmode.py
 @@ -20,6 +20,10 @@ def __init__(self, ohno): } self.ohno.paused = True + def __str__(self): + return 'debug' + __repr__ = __str__ + def get_cursor_idx(self): return self.cursor['y'] * 80 + self.cursor['x'] @@ -89,6 +93,3 @@ def second_botline(self): hero.dlvl, hero.hp, hero.maxhp, hero.ac, hero.level, hero.turns, hero.gold ) - - def __str__(self): - return 'debug'
7 ohno/ui/normalmode.py
 @@ -8,12 +8,13 @@ def __init__(self, ohno): self.ohno = ohno self.ohno.paused = False + def __str__(self): + return 'normal' + __repr__ = __str__ + def on_input(self, input): if input in '|sp': return super(NormalMode, self).on_input(input) elif input == 'd': from ohno.ui.debugmode import DebugMode return DebugMode - - def __str__(self): - return 'normal'