def test_update_rank(level, offset, expected, problems_dir): """Check whether updating the rank works correctly.""" p = Problems(problems_dir) p.level = level p.offset = offset p.update_rank() assert expected == (p.level, p.offset)
def load_problems(self, problems_dir, rank=None): """Load all problems found in the given directory. :param str problems_dir: where to look for tsumego :param str rank: the current rank of the user. """ if not rank and self.problems: rank = self.problems.pretty_rank self.problems = Problems(problems_dir, rank)
def test_get_problems(problem_files, problems_dir): """Check whether problem files are correctly found and returned.""" p = Problems(problem_files) problems = [ p._parse_problem(problems_dir / ('%d_kyu_%d.sgf' % (i, i))) for i in xrange(20) ] problems += [ p._parse_problem( problems_dir / ('[%d]%d_kyu_%d.sgf' % (i - 10, i, i + 20)) ) for i in xrange(20) ] for i in xrange(20): problem = {'rank': None} problem.update(p._parse_problem(problems_dir / ('%d.sgf' % i))) problems.append(problem) assert sorted(p._get_problems(problem_files)) == sorted(problems)
class GobanGrid(Grid, Goban): """A grid that handles a goban.""" def __init__(self, *args, **kwargs): """init this grid.""" self.comments_box = None self.load_problems( problems_dir=kwargs.pop( 'problems_dir', addon.getSetting('problems_dir')), rank=kwargs.pop('rank', addon.getSetting('rank')), ) super(GobanGrid, self).__init__(*args, **kwargs) def load_problems(self, problems_dir, rank=None): """Load all problems found in the given directory. :param str problems_dir: where to look for tsumego :param str rank: the current rank of the user. """ if not rank and self.problems: rank = self.problems.pretty_rank self.problems = Problems(problems_dir, rank) def setup_labels(self): """Set up all status messages and the comments box.""" window = self.window self.current_rank = window.getControl(ControlIds.rank) self.rating_box = window.getControl(ControlIds.rating) self.comments_box = window.getControl(ControlIds.comments) self.error_control = window.getControl(ControlIds.error) self.success_control = window.getControl(ControlIds.success) self.success_control.setLabel(_('solved')) self.error_control.setLabel(_('off_path')) self.update_comment() self.update_messages() @property def labels(self): """Get the labels of the current node from the board. The labels are white by default, so change the colour of any text to black if it was to be displayed on a white stone. """ for (x, y), label in Goban.labels.fget(self): if self.board.board[x][y] == 'w': label = '[COLOR black]%s[/COLOR]' % label yield ((x, y), label) def new_tile(self, x, y): """Add a new stone. :param int x: the column in which this stone is :param int y: the row that the stone is in """ stone = Stone(x, y, self, self.tile_width, self.tile_height) if self.board: stone.set_marker(self.board.board[x][y]) return stone def refresh_board(self): """Refresh the contents of the grid.""" if not self.grid: log('No grid found during board refresh', log.LOGDEBUG) return self.position_marker.setImage( get_image("shadow_%s.png" % self.next_player_name)) self.update_messages() self.update_labels() self.update_comment() # refresh all points for x in xrange(self.game.get_size()): for y in xrange(self.game.get_size()): pos = (x, y) if pos in self.marks: self.grid[x][y].mark('mark') elif pos in self.triangles: self.grid[x][y].mark('triangle') elif pos in self.squares: self.grid[x][y].mark('square') elif pos in self.circles: self.grid[x][y].mark('circle') elif pos in self.marks: self.grid[x][y].mark('mark') else: self.grid[x][y].mark() self.grid[x][y].set_player(self.board.board[x][y]) self.mark_hints() def set_size(self, size): """Set the board size and refresh what is displayed.""" if size <= 9: actual_size = 9 if self.rows > 9: self.window.getControl(ControlIds.goban).setImage('goban9.png') elif 9 < size <= 13: actual_size = 13 if 9 <= self.rows or self.rows > 13: self.window.getControl(ControlIds.goban).setImage('goban13.png') elif size > 13: actual_size = 19 if 13 <= self.rows: self.window.getControl(ControlIds.goban).setImage('goban19.png') super(GobanGrid, self).set_size(actual_size) # if the board is irregular (i.e. not 9x9, 13x13 or 19x19), make sure # that the pointer is correctly set, and that any tiles outside the # boundaries are correctly hidden if size < actual_size: self.rows = size self.columns = size current_x = self.current.x if self.current.x < size else size - 1 current_y = self.current.y if self.current.y < size else size - 1 self.select(self.grid[current_x][current_y]) # hide all tiles that shouldn't be shown for p1 in xrange(0, actual_size): for p2 in xrange(size, actual_size): self.grid[p1][p2].set_marker('wall') self.grid[p2][p1].set_marker('wall') def load(self, sgf=None): """Load the given SGF, or reload the current one if none provided. :param (str or None) sgf: the SGF to be loaded """ super(Grid, self).load(sgf) if self.game: self.reset() self.set_size(self.game.get_size()) self.refresh_board() def next(self): """Load the next problem. If no problem can be loaded, an error message is displayed. """ # loop over problems until a good one is found for problem in self.problems: self.problem = problem try: self.load(self.problem['sgf']) except (ValueError, IndexError): traceback.print_exc() else: self.current_rank.setText( _('current_rank') % self.problems.rank) if self.problem.get('rank'): rating = self.problem.get('rating') or 0 rank_value, rank = self.problem.get('rank') self.rating_box.setText( _('rating') % (rating, rank_value, rank)) else: self.rating_box.setText('') return else: level = self.problems.level ranks = map( lambda rank: '%d %s' % rank, map(self.problems.get_rank, [level + 3, level - 3]) ) self.comments_box.setText(_('no_problems_found') % tuple(ranks)) def reset(self): """Reset the board.""" self.hints = False self.position_marker.setImage( get_image("shadow_%s.png" % self.next_player_name)) self.update_messages() self.update_labels() def problem_solved(self, solved, weight=0.25): """Mark whether this problem was solved or not. :param boolean solved: whether the problem was solved :param float weight: the wieght of the solution. """ if 'solved' in self.problem: return if solved: self.problems.success(self.problem['rank'], weight) else: self.problems.failure(self.problem['rank'], weight) self.problem['solved'] = solved addon.setSetting('level', self.problems.pretty_rank) def toggle_hints(self, state=None): """Toggle the display of hints on the board. :param boolean state: this can be used to force the state :returns: whether or not hints are shown """ self.problem_solved(False, 0.4) self.hints = state if state is not None else not self.hints self.refresh_board() return self.hints def update_comment(self, comment=None): """Set the comment to the given comment, or the current SGF comment. If no comment is provided, the comment of the current SGF node will be displayed. :param str or None comment: the comment to be displayed """ if not self.comments_box: log('No comments box found during comment refresh', log.LOGDEBUG) return if comment is None: comment = self.current_comment.replace('FORCE', '').replace('RIGHT', '') # noqa self.comments_box.setText(comment) def update_messages(self): """Update the status messages' visibility.""" self.error_control.setVisible(bool(self.board and not self.on_path)) self.success_control.setVisible(bool(self.board and self.correct)) def mark_hints(self): """Mark all hints on the board.""" if not self.hints: return def mark_node(node, mark=None): """Mark a single hint on the board. :param str mark: what the marker should be """ _, (x, y) = node.get_move() self.grid[x][y].set_marker(mark) # get rid of any previous markers - the grandparent must be used, # because the parent is automatically placed and has no hints if self.node.parent and self.node.parent.parent: for node in filter(lambda v: v != self.node, self.node.parent.parent): _, (x, y) = node.get_move() self.grid[x][y].set_image() for child in self.node: mark_node(child, 'good' if self.correct_path(child) else 'bad') def handle_key(self, key): """Handle the given key. :param int key: the key code :returns: whether the action was handled """ if not self.board: return if key in SELECT: self.move(*self.current.pos) self.random_move() if self.correct: self.problem_solved(True) elif key in BACK: self.problem_solved(False) if not self.correct and self.on_path: self.back() self.back() elif key in hoshi: x, y = hoshi[key] self.current = self.grid[x][y] self.position_marker.setPosition(*self.current.display_pos) else: return False self.refresh_board() return True
def test_parse_rank(rank_str, rank, problems_dir): """Test whether rank strings are correctly parsed.""" p = Problems(problems_dir) assert p._parse_level(rank_str) == rank
def test_invalid_parse_problem(filename, problems_dir): """Check whether invalid names return None.""" p = Problems(problems_dir) assert p._parse_problem(filename) is None
def test_unparsable_parse_problem(filename, problems_dir): """Check whether names that cannot be parsed only return the filename.""" p = Problems(problems_dir) assert p._parse_problem(filename) == {'problem_file': filename}
def test_parse_problem(filename, result, problems_dir): """Test whether filenames get parsed correctly.""" p = Problems(problems_dir) result['problem_file'] = filename assert p._parse_problem(filename) == result
def test_rank(level, rank, problems_dir): """Check whether the rank is correctly calculated.""" p = Problems(problems_dir) p.level = level assert p.rank == rank == p.get_rank(level) assert p.pretty_rank == '%d %s' % rank