summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Anderson <jandew+dev@gmail.com>2016-02-24 09:47:09 -0800
committerJoe Anderson <jandew+dev@gmail.com>2016-02-24 10:06:51 -0800
commit6127f0006336e2de2a3082069acedf63226ea6a0 (patch)
tree75aa69e4759bf1a92b89759073a682d4ec122261
parent7403b36992547841851e2ed405a5500a82416919 (diff)
downloadoo-6127f0006336e2de2a3082069acedf63226ea6a0.tar.gz
oo-6127f0006336e2de2a3082069acedf63226ea6a0.zip
major docstring revision
-rw-r--r--oo.py195
1 files 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