diff options
Diffstat (limited to 'oo.py')
-rw-r--r-- | oo.py | 182 |
1 files changed, 121 insertions, 61 deletions
@@ -6,29 +6,11 @@ class ooPuzzle: No rendering information is stored or interpreted here. - The following two attributes are to be accessed externally, - though editing them should be done through methods if possible: - - 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) - - inverted_pieces is a closely related mapping to pieces, - where all edges swap filled state - NOT IMPLEMENTED - - 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) - - All other attributes are intended to be accessed through methods. + All attributes are intended to be accessed through methods. If direct access is necessary, here are their explanations: + (the following attributes are constant) + DIRECTIONS is a dictionary mapping: each direction as a string to a direction index in range(4) @@ -44,7 +26,7 @@ class ooPuzzle: PIECE_ORIENT_TO_EDGES is an inverse of the previous mapping. - (the following are attributes set by __init__) + (the following attributes are set by __init__) X is an integer for the range of x-positions @@ -55,7 +37,27 @@ class ooPuzzle: 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) + + 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) + + inverted_pieces is a closely related mapping to pieces, + where all edges swap filled state + NOT IMPLEMENTED + + 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): """Create a new ooPuzzle instance. @@ -137,6 +139,70 @@ class ooPuzzle: (1, 1, 1, 1) : (5, 0) } + #### + # + # The following methods are low-level, + # and require position inputs to be forced in range. + # + #### + + def get_piece(self, x, y): + x %= self.X + y %= self.Y + return self.pieces[x, y] + + def get_orient(self, x, y): + x %= self.X + y %= self.Y + return self.orients[x, y] + + def get_piece_orient(self, x, y): + x %= self.X + y %= self.Y + return self.pieces[x, y], self.orients[x, y] + + def get_adj_pos(self, x0, y0, direction, + return_is_internal=False): + """Returns the position adjacent to (x0, y0) in direction. + + direction may be any string or integer in the dictionary + self.DIRECTIONS (case insensitive) + + If return_is_internal, then a third output is returned, + a bool for whether both edges are internal. + (If false, then the edges are across a border.) + """ + x0 %= self.X + y0 %= self.Y + if type(direction) is str: + direction = self.DIRECTIONS[direction.upper()] + dx, dy = self.DX_DY[direction] + x1 = (x0 + dx) % self.X + y1 = (y0 + dy) % self.Y + + if return_is_internal: + is_internal = ( + x0 + dx == x1 and + y0 + dy == y1 ) + return x1, y1, is_internal + + return x1, y1 + + 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 + def set_pieces_from_edges(self, horiz_edges, vert_edges): """Convert edge dictionaries into a puzzle state. @@ -233,42 +299,26 @@ class ooPuzzle: 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.) + # + #### + def rotate_cw(self, x, y, turns=1): """Rotates the piece at (x, y) by clockwise turns.""" - o = self.orients[x, y] + o = self.get_orient(x, y) o = (o + turns) % 4 - self.orients[x, y] = o + self.set_orient(x, y, o) def random_orients(self): """Randomly rotate the current pieces.""" for x in range(self.X): for y in range(self.Y): - self.orients[x, y] = random.randrange(4) - - def position_in_direction(self, x0, y0, direction, - return_is_internal=False): - """Returns the position adjacent to (x0, y0) in direction. - - direction may be any string or integer in the dictionary - self.DIRECTIONS (case insensitive) - - If return_is_internal, then a third output is returned, - 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()] - dx, dy = self.DX_DY[direction] - x1 = (x0 + dx) % self.X - y1 = (y0 + dy) % self.Y - - if return_is_internal: - is_internal = ( - x0 + dx == x1 and - y0 + dy == y1 ) - return x1, y1, is_internal - - return x1, y1 + 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. @@ -279,7 +329,7 @@ class ooPuzzle: 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 in direction of (x0, y0), + 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. @@ -288,13 +338,13 @@ class ooPuzzle: if type(direction) is str: direction = self.DIRECTIONS[direction.upper()] - piece = self.pieces [x0, y0] - orient = self.orients[x0, y0] + piece, orient = self.get_piece_orient(x0, y0) edge0 = self.PIECE_ORIENT_TO_EDGES[piece, orient][direction] - x1, y1, is_internal = self.position_in_direction(x0, y0, direction, True) - piece = self.pieces [x1, y1] - orient = self.orients[x1, y1] + 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] @@ -404,27 +454,33 @@ class ooPlay: "┝┯┥┷", "┿┿┿┿"] - def display_subroutine(self, x, y): + def display_subroutine(self, x, y, recursing=False): """Update one position on the board.""" #TODO: use inverted_pieces and not_inverted_pieces # if self.inverted #TODO: quadruple the display of the puzzle # if self.puzzle.toroidal - piece = self.puzzle.pieces [x, y] - orient = self.puzzle.orients[x, y] + piece, orient = self.puzzle.get_piece_orient(x, y) string = self.PIECE_ORIENT_TO_STRING[piece][orient] is_error = False if self.show_errors: is_error = not self.puzzle.check_piece(x, y) color = curses.color_pair((x + y) % 2 + 2*is_error) self.screen.addstr(y, x, string, color) + if self.puzzle.toroidal and not recursing: + X, Y = self.puzzle.X, self.puzzle.Y + x1 = (x + X) % (2*X) + y1 = (y + Y) % (2*Y) + self.display_subroutine(x1, y , recursing=True) + self.display_subroutine(x , y1, recursing=True) + self.display_subroutine(x1, y1, recursing=True) def display_pos(self, x, y): """Update one position on the board, refresh screen.""" self.display_subroutine(x, y) if self.show_errors: for direction in self.puzzle.DIRECTIONS: - x1, y1 = self.puzzle.position_in_direction(x, y, direction) + x1, y1 = self.puzzle.get_adj_pos(x, y, direction) self.display_subroutine(x1, y1) self.screen.refresh() @@ -449,7 +505,8 @@ class ooPlay: if pause is not given but a string is, there will be a pause if neither a pause nor a string is given, there will not be a pause - ooPlay.pause_length is the number of milliseconds per character to pause + ooPlay.pause_length + is the number of milliseconds per character to pause """ if string == None: string = "H" @@ -559,6 +616,9 @@ class ooPlay: self.show_errors = not self.show_errors self.display() self.write() + #TODO: add in show solution, with undo option + #TODO: add in "Xx" for extra hard mode + # which has no blank or full pieces elif inp in "Ii": self.write("Puzzle is now" + " NOT"*self.inverted |