summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Anderson <jandew+dev@gmail.com>2016-02-17 04:56:54 -0800
committerJoe Anderson <jandew+dev@gmail.com>2016-02-17 04:56:54 -0800
commit1760a17b59c0209b19b9513522a499000aac1b15 (patch)
tree5993b4daf5d25e6f25abee5e2514f196cebcaf31
parentb41b1f9066e611ca617c3dd71ddeedb4dde55131 (diff)
downloadoo-1760a17b59c0209b19b9513522a499000aac1b15.tar.gz
oo-1760a17b59c0209b19b9513522a499000aac1b15.zip
toroidal display implemented
-rw-r--r--oo.py182
1 files changed, 121 insertions, 61 deletions
diff --git a/oo.py b/oo.py
index d304374..98f52e6 100644
--- a/oo.py
+++ b/oo.py
@@ -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