class Screen: """ Represents drawable part of the terminal, where other parts (glass, figures) are drawed. """ glass = None def init(self): """ Initialize curses. """ # Initialize ncurses library. self.stdscr = curses.initscr() curses.noecho() curses.cbreak() self.stdscr.keypad(1) curses.curs_set(0) self.width = 28 self.height = 24 # Status message. Eg. "Paused" self.status = None self.level = 1 self.score = 0 self.lines = 0 # Draw main window. self.window = curses.newwin(self.height, self.width, 0, 0) self.window.border() self.window.refresh() self.window.nodelay(1) self.glass = Glass(self) self.next_figure = None def destroy(self): """ Free ncurses library resources and back screen to normal mode. """ # Release ncurses. curses.nocbreak() self.stdscr.keypad(0) curses.echo() curses.endwin() curses.curs_set(1) def move_figure_to_start(self, figure): """ Move figure to start position. Figure shoud appears at the middle top of the glass. """ figure.y = 0 figure.x = math.floor(self.glass.width / 2) def draw(self, figure): """ Redraw the screen. """ y_base = 10 x_base = self.glass.width + 4 self.window.move(y_base, x_base) self.window.addstr("Score:" + "{0:>6}".format(self.score)) self.window.move(y_base + 1, x_base) self.window.addstr("Lines:" + "{0:>6}".format(self.lines)) self.window.move(y_base + 2, x_base) self.window.addstr("Level:" + "{0:>6}".format(self.level)) self.window.move(y_base + 4, x_base) if self.status: self.window.addstr(self.status, curses.A_BLINK) else: self.window.addstr(" " * (self.width - 1 - x_base)) self.glass.clear() self.glass.draw() # # Draw next picture preview. # # Clear place before. for y in range(self.preview_figure.y - 2, self.preview_figure.y + 3): for x in range(self.preview_figure.x - 2, self.preview_figure.x + 3): self.window.move(y, x) self.window.addstr(" ") self.preview_figure.draw(self.window, self.width, self.height) figure.draw(self.glass.window, self.glass.width, self.glass.height) def set_next_figure(self, figure): """ Set next falling figure for preview. """ self.preview_figure = copy.copy(figure) self.preview_figure.x = self.glass.width + 7 self.preview_figure.y = 5 def getch(self): """ Return pressed key. """ return self.window.getch() def move_figure_down(self, figure): """ Move figure down for one position, -- falling. Returns False if figure was joined to lees. """ if self._is_figure_down_moveable(figure): figure.y += 1 return True else: # Figure should be joined to lees. self.glass.lees_figure(figure) return False def _is_figure_down_moveable(self, figure): """ Returns True if figure can be moved down. """ bottoms = figure.get_bottom_positions() for i, y in enumerate(bottoms): lees_top = self.glass.get_lees_top(figure.x - 2 + i) if y >= lees_top - 1 or y >= self.glass.height: return False return True def move_figure_right(self, figure): """ Move figure one position right. """ if self._is_figure_right_moveable(figure): figure.x += 1 def move_figure_left(self, figure): """ Move figure one position left. """ if self._is_figure_left_moveable(figure): figure.x -= 1 def _is_figure_right_moveable(self, figure): """ Returns True if this figure can be moved right at least one step. """ right_edge = figure.get_right_edge() sprite = figure.get_sprite() return right_edge < self.glass.width - 1 and self._is_space_free(figure.y, figure.x + 1, sprite) def _is_figure_left_moveable(self, figure): """ Returns True if this figure can be moved left at least one step. """ left_edge = figure.get_left_edge() sprite = figure.get_sprite() return left_edge > 0 and self._is_space_free(figure.y, figure.x - 1, sprite) def rotate_figure_anticlockwise(self, figure): """ Rotate figure contraclockwise. """ i = figure.get_prev_sprite_index() # We can't rotate if figure will be outside the glass. right_edge = figure.get_right_edge(i) left_edge = figure.get_left_edge(i) if right_edge >= self.glass.width or left_edge < 0: return # Or figure will be under the glass's floor. bottoms = figure.get_bottom_positions(i) for y in bottoms: if y >= self.glass.height: return # And we have free place for future sprite. i = figure.get_prev_sprite_index() sprite = figure.get_sprite(i) if not self._is_space_free(figure.y, figure.x, sprite): return figure.rotate_anticlockwise() def rotate_figure_clockwise(self, figure): """ Rotate figure clockwise, if it can be rotated. """ i = figure.get_next_sprite_index() # Block rotation if figure will be moved outside the glass. right_edge = figure.get_right_edge(i) left_edge = figure.get_left_edge(i) if right_edge >= self.glass.width or left_edge < 0: return # Or under the floor. bottoms = figure.get_bottom_positions(i) for y in bottoms: if y >= self.glass.height: return # And we have free place for the next sprite. i = figure.get_next_sprite_index() sprite = figure.get_sprite() if not self._is_space_free(figure.y, figure.x, sprite): return figure.rotate_clockwise() def _is_space_free(self, y, x, sprite): """ Returns True if space for the given sprite is fee (without lees) in glass. """ for yy, row in enumerate(sprite): for xx, cel in enumerate(row): if cel > 0: if not self.glass.is_space_free(y - 2 + yy, x - 2 + xx): return False return True def delete_full_lines(self): """ Remove full lines and returns their count. """ return self.glass.delete_full_lines()