From 6127f0006336e2de2a3082069acedf63226ea6a0 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 24 Feb 2016 09:47:09 -0800 Subject: major docstring revision --- oo.py | 195 +++++++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 123 insertions(+), 72 deletions(-) diff --git a/oo.py b/oo.py index d9a153e..ebfceda 100644 --- a/oo.py +++ b/oo.py @@ -6,6 +6,31 @@ class ooPuzzle: No rendering information is stored or interpreted here. + The following are all the get and set methods, + categorized with their inverses: + + DATA CALLS + get_piece set_piece + get_orient set_orient + get_piece_orient set_piece_orient + + HELPER METHODS + get_adj_pos + get_edge_pair + + INITIALIZATION + set_pieces_from_game_id + set_pieces_from_edges + + The following are all the user interface methods. + For more description, see their documentation. + + rotate_cw : rotates a piece clockwise + random_orients : randomly orients all pieces + check_edge_pair : verifies an edge pair matches properly + check_piece : verifies edges on a piece connect to other pieces + is_solved : verifies all edges on all pieces are connected + All attributes are intended to be accessed through methods. If direct access is necessary, here are their explanations: @@ -32,18 +57,23 @@ class ooPuzzle: Y is an integer for the range of y-positions - game_id is an integer used by set_pieces_from_game_id - It uniquely corresponds to the solution. - toroidal is a bool indicating whether the border of the puzzle loops back to the opposite side - (the following attributes are variable during normal use) + game_id is an integer + It uniquely corresponds to the solution in edges. + if toroidal: in range(2**( 2*X*Y )) + if not toroidal: in range(2**( (X-1)*Y + X*(Y-1) )) pieces is a dictionary mapping: each position of form (x in range(self.X), y in range(self.Y)) to a piece id in range(6) + + orients is a dictionary mapping: + each position of form (x in range(self.X), + y in range(self.Y)) + to a direction index in range(4) inverted_pieces is a closely related mapping to pieces, where all edges swap filled state @@ -51,11 +81,6 @@ class ooPuzzle: not_inverted_pieces is similar, but only swaps border edges NOT IMPLEMENTED - - orients is a dictionary mapping: - each position of form (x in range(self.X), - y in range(self.Y)) - to a direction index in range(4) """ def __init__(self, X, Y, game_id=None, toroidal=False): @@ -63,14 +88,23 @@ class ooPuzzle: Arguments. X,Y : horizontal and vertical size of the puzzle - game_id : passed to ooPuzzle.set_pieces_from_game_id + game_id : int corresponding to solution + : if None, a random game_id is generated toroidal : bool for looping around the end of the puzzle """ self.X, self.Y = X, Y + self.game_id = game_id self.toroidal = toroidal + self.pieces = {} self.orients = {} - self.set_pieces_from_game_id(game_id) + self.set_data_from_game_id(random_game_id = (game_id == None)) + + #### + # + # The following are constants for converting between various data sets. + # + #### # Changing conceptual directions into indices. DIRECTIONS = {'LEFT' : 0, @@ -141,8 +175,8 @@ class ooPuzzle: #### # - # The following methods are low-level, - # and require position inputs to be forced in range. + # The following get and set methods are low-level data calls, + # and force the input positions into the appropriate range. # #### @@ -161,6 +195,30 @@ class ooPuzzle: y %= self.Y return self.pieces[x, y], self.orients[x, y] + def set_piece(self, x, y, value): + x %= self.X + y %= self.Y + self.pieces[x, y] = value + + def set_orient(self, x, y, value): + x %= self.X + y %= self.Y + self.orients[x, y] = value + + def set_piece_orient(self, x, y, value): + x %= self.X + y %= self.Y + self.pieces[x, y], self.orients[x, y] = value + + #### + # + # The following two get methods are helper methods. + # + # get_adj_pos is low-level, forcing inputs into the appropriate range. + # get_edge_pair is high-level, using low-level methods when needed. + # + #### + def get_adj_pos(self, x0, y0, direction, return_is_internal=False): """Returns the position adjacent to (x0, y0) in direction. @@ -187,21 +245,47 @@ class ooPuzzle: return x1, y1, is_internal return x1, y1 + + def get_edge_pair(self, x0, y0, direction): + """Returns the edge pair's filled status in direction and is_internal. - def set_piece(self, x, y, value): - x %= self.X - y %= self.Y - self.pieces[x, y] = value + direction may be any string or integer in the dictionary + self.DIRECTIONS (case insensitive) - def set_orient(self, x, y, value): - x %= self.X - y %= self.Y - self.orients[x, y] = value + The first output is the status of the edge of (x0, y0) in direction. - def set_piece_orient(self, x, y, value): - x %= self.X - y %= self.Y - self.pieces[x, y], self.orients[x, y] = value + The second output is the status of the adjacent edge, + that is, of the piece adjacent to (x0, y0) in direction, + the status of the edge in the opposite direction. + + The third output is a bool for whether both edges are internal. + (If false, then the edges are across a border.) + """ + if type(direction) is str: + direction = self.DIRECTIONS[direction.upper()] + + piece, orient = self.get_piece_orient(x0, y0) + edge0 = self.PIECE_ORIENT_TO_EDGES[piece, orient][direction] + + x1, y1, is_internal = \ + self.get_adj_pos(x0, y0, direction, + return_is_internal=True) + piece, orient = self.get_piece_orient(x1, y1) + direction = (direction + 2) % 4 + edge1 = self.PIECE_ORIENT_TO_EDGES[piece, orient][direction] + + return edge0, edge1, is_internal + + #### + # + # The following get and set methods are for initialization. + # + # They allow any of the following three data sets to set the other two: + # game_id : self.game_id : int + # edges : self.horiz_edges, self.vert_edges : dicts + # pieces : self.pieces, self.orients : dicts + # + #### def set_pieces_from_edges(self, horiz_edges, vert_edges): """Convert edge dictionaries into a puzzle state. @@ -236,18 +320,19 @@ class ooPuzzle: self.pieces [x, y] = piece self.orients[x, y] = orient - def set_pieces_from_game_id(self, game_id=None): + def set_pieces_from_game_id(self, random_game_id=False): """Convert game_id value into a solution puzzle state. - game_id is an integer - if toroidal: in range(2**( 2*X*Y )) - if not toroidal: in range(2**( (X-1)*Y + X*(Y-1) )) - if None, then a random game_id is generated + if random_game_id, + then a random game_id is chosen from the appropriate range, + and pieces are set from that """ # compute: # n_horiz, the number of horizontal edge pairs # n_vert, the number of vertical edge pairs + # + # generate random game_id if needed shift = -int(not self.toroidal) n_col = self.X + shift @@ -255,11 +340,8 @@ class ooPuzzle: n_horiz = n_col * self.Y n_vert = self.X * n_row - # generate and record game_id - - if game_id == None: - game_id = random.randrange(0, 2**(n_horiz + n_vert)) - self.game_id = game_id + if random_game_id: + self.game_id = random.randrange(0, 2**(n_horiz + n_vert)) # prepare to record edges for self.set_pieces_from_edges @@ -277,6 +359,8 @@ class ooPuzzle: # set the edges determined by game_id + game_id = self.game_id + for i in range(n_vert): row, x = divmod(i, self.X) game_id, bit = divmod(game_id, 2) @@ -295,16 +379,13 @@ class ooPuzzle: for x in range(self.X): vert_edges[x, 0] = vert_edges[x, self.Y] - # turn edges into pieces + # set the pieces from the edges self.set_pieces_from_edges(horiz_edges, vert_edges) #### # - # The following methods are high-level, - # and no longer require position inputs to be forced in range. - # - # (They call the low-level functions when needed.) + # The rest of the methods are for user interface. # #### @@ -319,36 +400,6 @@ class ooPuzzle: for x in range(self.X): for y in range(self.Y): self.set_orient(x, y, random.randrange(4)) - - def get_edge_pair(self, x0, y0, direction): - """Returns the edge pair's filled status in direction and is_internal. - - direction may be any string or integer in the dictionary - self.DIRECTIONS (case insensitive) - - The first output is the status of the edge of (x0, y0) in direction. - - The second output is the status of the adjacent edge, - that is, of the piece adjacent to (x0, y0) in direction, - the status of the edge in the opposite direction. - - The third output is a bool for whether both edges are internal. - (If false, then the edges are across a border.) - """ - if type(direction) is str: - direction = self.DIRECTIONS[direction.upper()] - - piece, orient = self.get_piece_orient(x0, y0) - edge0 = self.PIECE_ORIENT_TO_EDGES[piece, orient][direction] - - x1, y1, is_internal = \ - self.get_adj_pos(x0, y0, direction, - return_is_internal=True) - piece, orient = self.get_piece_orient(x1, y1) - direction = (direction + 2) % 4 - edge1 = self.PIECE_ORIENT_TO_EDGES[piece, orient][direction] - - return edge0, edge1, is_internal def check_edge_pair(self, x, y, direction, if_filled=False): """Check the edge pair's validity around (x, y) in direction. @@ -390,7 +441,7 @@ class ooPlay: """Create a new ooPlay instance. Arguments. - scr : curses screen object used for display + screen : curses screen object used for display """ self.screen = screen self.Y, self.X = self.screen.getmaxyx() @@ -465,7 +516,7 @@ class ooPlay: break else: break - self.puzzle.set_pieces_from_game_id() + self.puzzle.set_pieces_from_game_id(random_game_id=True) self.puzzle.random_orients() # reset some flags -- cgit v1.2.3-54-g00ecf