def __init__(self, quotes, stats): self.config = Config() self.stats = stats self.average = self.stats.average(self.stats.tag, last_n=10) self.tab_spaces = None # Stats self.position = 0 self.incorrect = 0 self.total_incorrect = 0 self.start = None self.stop = None self._edit = "" self.num_quotes = len(quotes) self.quotes = quotes.random_iterator() self.screen = Screen() self.set_quote(self.quotes.next())
def __init__(self, quotes, stats, cpm_flag, monochrome, hard_flag, redlist_flag, redlist_threshold): self.config = Config() self.stats = stats self.cpm_flag = cpm_flag self.hard_flag = hard_flag self.redlist_flag = redlist_flag self.average = self.stats.average(self.stats.tag, last_n=10) self.tab_spaces = None self.redlist = Quotes.load_redlist() self.redlist_threshold = redlist_threshold if redlist_flag and not self.redlist: print("Redlist is empty") exit(0) # Stats self.position = 0 self.incorrect = 0 self.total_incorrect = 0 self.cheight = 0 self.start = None self.stop = None self._edit = "" self.num_quotes = len(quotes) self.quotes = quotes.random_iterator() if self.redlist_flag: self.quotes.set_indices(self.redlist) self.screen = Screen(monochrome) self.set_quote(self.quotes.next()) self.now = time.time()
def __init__(self, quotes, stats, cpm_flag, monochrome): self.config = Config() self.stats = stats self.cpm_flag = cpm_flag self.average = self.stats.average(self.stats.tag, last_n=10) self.tab_spaces = None # Stats self.position = 0 self.incorrect = 0 self.total_incorrect = 0 self.cheight = 0 self.start = None self.stop = None self._edit = "" self.num_quotes = len(quotes) self.quotes = quotes.random_iterator() self.screen = Screen(monochrome) self.set_quote(self.quotes.next()) self.now = time.time()
def __init__(self, quotes, stats, cpm_flag): self.config = Config() self.stats = stats self.cpm_flag = cpm_flag self.average = self.stats.average(self.stats.tag, last_n=10) self.tab_spaces = None # Stats self.position = 0 self.incorrect = 0 self.total_incorrect = 0 self.start = None self.stop = None self._edit = "" self.num_quotes = len(quotes) self.quotes = quotes.random_iterator() self.screen = Screen() self.set_quote(self.quotes.next())
class GameManager(object): """The main game runner.""" def __init__(self, quotes, stats): self.config = Config() self.stats = stats self.average = self.stats.average(self.stats.tag, last_n=10) self.tab_spaces = None # Stats self.position = 0 self.incorrect = 0 self.total_incorrect = 0 self.start = None self.stop = None self._edit = "" self.num_quotes = len(quotes) self.quotes = quotes.random_iterator() self.screen = Screen() self.set_quote(self.quotes.next()) def __enter__(self): return self def __exit__(self, error_type, error_value, error_traceback): self.screen.deinit() if error_type is not None: return False return self def set_tab_spaces(self, spaces): """Sets how many spaces a tab should expand to.""" self.tab_spaces = spaces def mark_finished(self): """Marks current race as finished.""" self.stop = time.time() self.stats.add(self.wpm(self.elapsed), self.accuracy, self.quote.text_id, self.quotes.database) self.average = self.stats.average(self.stats.tag, last_n=10) def set_quote(self, quote): """Sets current quote.""" self.quote = quote self.recorder = Recorder() self.screen.set_quote(self.quote) @property def is_typing(self): """Is user currently typing a quote?""" return (self.start is not None) and (self.stop is None) @property def game_done(self): """Has user finished a quote?""" return (self.start is not None) and (self.stop is not None) def run(self, to_front=None): """Starts the main game loop.""" self.set_tab_spaces(self.config.wpm.tab_spaces) if to_front: self.quotes.put_to_front(to_front) self.set_quote(self.quotes.current()) key = None while True: self.now = time.time() head = self.get_stats(self.elapsed) if self.is_typing: if self.screen.first_key: self.screen.first_key = False self.recorder.reset() self.screen.rerender_race(head) self.screen.show_keystroke(head, self.position, self.incorrect, self._edit, key) elif self.game_done: self.screen.show_score(head, self.wpm(self.elapsed), self.stats) else: self.screen.show_browser(head, self.stats) self.screen.window.refresh() key = self.screen.get_key() self.handle_key(key) def wpm(self, elapsed): """Words per minute.""" if self.start is None: return 0 return min((60.0 * self.position / 5.0) / elapsed, 999) def cps(self, elapsed): """Characters per second.""" if self.start is None: return 0 return min(float(self.position) / elapsed, 99) @property def elapsed(self): """Elapsed game round time.""" if self.start is None: # Typing has not started return 0 if self.stop is None: # Currently typing return self.now - self.start # Done typing return self.stop - self.start @property def accuracy(self): """Returns typing accuracy.""" if self.start is None: return 0 length = len(self.quote.text) return float(length) / (length + self.total_incorrect) def get_stats(self, elapsed): """Returns the top-bar stats line.""" kbd = self.stats.tag parts = ( "%5.1f wpm" % self.wpm(elapsed), " %4.1f cps" % self.cps(elapsed), " %5.2fs" % elapsed, " %5.1f%% acc" % (100.0 * self.accuracy), " %5.1f avg wpm" % self.average, " - " + (kbd if kbd is not None else "Unspecified"), ) stat = "" for part in parts: if (len(stat) + len(part)) <= self.screen.columns: stat += part return stat def reset(self, direction=0): """Cancels current game.""" self.start = None self.stop = None self.position = 0 self.incorrect = 0 self.total_incorrect = 0 self.cheight = 0 self._edit = "" self.screen.first_key = True self.screen.clear_prompt() if direction: if direction > 0: self.set_quote(self.quotes.next()) else: self.set_quote(self.quotes.previous()) self.screen.clear() def resize(self): """Handles a resized terminal.""" max_y, max_x = self.screen.window.getmaxyx() self.screen.clear() # Check if we have the resizeterm ncurses extension if hasattr(curses, "resizeterm"): curses.resizeterm(max_y, max_x) # An ungetch for KEY_RESIZE will be sent to let others handle it. # We'll just pop it off again to prevent endless loops. self.screen.get_key() self.screen.set_quote(self.quote) if self.start is not None and self.stop is None: # Resize during typing requires redrawing quote. self.screen.update_quote(Screen.COLOR_QUOTE) self.screen.update_author() if self.position + self.incorrect <= len(self.quote.text): for pos in range(self.position + 1): self.screen.highlight_progress(pos, 0) for inc in range(self.incorrect + 1): self.screen.highlight_progress(self.position, inc) def handle_key(self, key): """Dispatches actions based on key and current mode.""" # TODO: Refactor this mess of a function if key is None: return if key == "KEY_RESIZE": self.resize() return # Browse mode if self.start is None or (self.start is not None and self.stop is not None): if key in (" ", "KEY_LEFT", "KEY_RIGHT"): self.reset(direction=-1 if key == "KEY_LEFT" else 1) return elif Screen.is_escape(key): # Exit program raise KeyboardInterrupt() if Screen.is_escape(key): self.reset() return self.recorder.add(self.elapsed, key, self.position, self.incorrect) if Screen.is_backspace(key): if self.incorrect: self.incorrect -= 1 self._edit = self._edit[:-1] elif self._edit: self.position -= 1 self._edit = self._edit[:-1] return if self.stop is not None: # Use wants to try again immediately after score self.reset() head = self.get_stats(self.elapsed) self.screen.rerender_race(head) self.screen.show_keystroke(head, self.position, self.incorrect, self._edit, key) # Start recording upon first ordinary key press if self.start is None: self.start = time.time() if key == curses.KEY_ENTER: key = "\n" elif key == "\t" and self.tab_spaces is not None: key = " " * self.tab_spaces # Did the user strike the correct key? if self.incorrect == 0 and self.quote.text[self.position] == key: self.position += 1 # Reset edit buffer on a correctly finished word if key == " " or key == "\n": self.screen.clear_prompt() self._edit = "" else: self._edit += key # Finished typing? if self.position == len(self.quote.text): self.mark_finished() elif self.incorrect + self.position < len(self.quote.text): self.incorrect += 1 self.total_incorrect += 1 if key == "\n": key = " " self._edit += key
def handle_key(self, key): """Dispatches actions based on key and current mode.""" # TODO: Refactor this mess of a function if key is None: return if key == "KEY_RESIZE": self.resize() return # Browse mode if self.start is None or (self.start is not None and self.stop is not None): if key in (" ", "KEY_LEFT", "KEY_RIGHT"): self.reset(direction=-1 if key == "KEY_LEFT" else 1) return elif Screen.is_escape(key): # Exit program raise KeyboardInterrupt() if Screen.is_escape(key): self.reset() return self.recorder.add(self.elapsed, key, self.position, self.incorrect) if Screen.is_backspace(key): if self.incorrect: self.incorrect -= 1 self._edit = self._edit[:-1] elif self._edit: self.position -= 1 self._edit = self._edit[:-1] return if self.stop is not None: # Use wants to try again immediately after score self.reset() head = self.get_stats(self.elapsed) self.screen.rerender_race(head) self.screen.show_keystroke(head, self.position, self.incorrect, self._edit, key) # Start recording upon first ordinary key press if self.start is None: self.start = time.time() if key == curses.KEY_ENTER: key = "\n" elif key == "\t" and self.tab_spaces is not None: key = " " * self.tab_spaces # Did the user strike the correct key? if self.incorrect == 0 and self.quote.text[self.position] == key: self.position += 1 # Reset edit buffer on a correctly finished word if key == " " or key == "\n": self.screen.clear_prompt() self._edit = "" else: self._edit += key # Finished typing? if self.position == len(self.quote.text): self.mark_finished() elif self.incorrect + self.position < len(self.quote.text): self.incorrect += 1 self.total_incorrect += 1 if key == "\n": key = " " self._edit += key
class GameManager(object): """The main game runner.""" def __init__(self, quotes, stats, cpm_flag): self.config = Config() self.stats = stats self.cpm_flag = cpm_flag self.average = self.stats.average(self.stats.tag, last_n=10) self.tab_spaces = None # Stats self.position = 0 self.incorrect = 0 self.total_incorrect = 0 self.start = None self.stop = None self._edit = "" self.num_quotes = len(quotes) self.quotes = quotes.random_iterator() self.screen = Screen() self.set_quote(self.quotes.next()) def __enter__(self): return self def __exit__(self, error_type, error_value, error_traceback): self.screen.deinit() if error_type is not None: return False return self def set_tab_spaces(self, spaces): """Sets how many spaces a tab should expand to.""" self.tab_spaces = spaces def mark_finished(self): """Marks current race as finished.""" self.stop = time.time() self.stats.add(self.wpm(self.elapsed), self.accuracy, self.quote.text_id, self.quotes.database) self.average = self.stats.average(self.stats.tag, last_n=10) def set_quote(self, quote): """Sets current quote.""" self.quote = quote self.recorder = Recorder() self.screen.set_quote(self.quote) @property def is_typing(self): """Is user currently typing a quote?""" return (self.start is not None) and (self.stop is None) @property def game_done(self): """Has user finished a quote?""" return (self.start is not None) and (self.stop is not None) def run(self, to_front=None): """Starts the main game loop.""" self.set_tab_spaces(self.config.wpm.tab_spaces) if to_front: self.quotes.put_to_front(to_front) self.set_quote(self.quotes.current()) key = None while True: self.now = time.time() head = self.get_stats(self.elapsed) if self.is_typing: if self.screen.first_key: self.screen.first_key = False self.recorder.reset() self.screen.rerender_race(head) self.screen.show_keystroke(head, self.position, self.incorrect, self._edit, key) elif self.game_done: self.screen.show_score(head, self.wpm(self.elapsed), self.stats, self.cpm_flag) else: self.screen.show_browser(head, self.stats, self.cpm_flag) self.screen.window.refresh() key = self.screen.get_key() self.handle_key(key) def wpm(self, elapsed): """Words per minute.""" if self.start is None: return 0 return min((60.0 * self.position / 5.0) / elapsed, 999) def cps(self, elapsed): """Characters per second.""" if self.start is None: return 0 return min(float(self.position) / elapsed, 99) @property def elapsed(self): """Elapsed game round time.""" if self.start is None: # Typing has not started return 0 if self.stop is None: # Currently typing return self.now - self.start # Done typing return self.stop - self.start @property def accuracy(self): """Returns typing accuracy.""" if self.start is None: return 0 length = len(self.quote.text) return float(length) / (length + self.total_incorrect) def get_stats(self, elapsed): """Returns the top-bar stats line.""" kbd = self.stats.tag parts = ( "%5.1f wpm" % self.wpm(elapsed), " %4.1f cps" % self.cps(elapsed), " %5.2fs" % elapsed, " %5.1f%% acc" % (100.0*self.accuracy), " %5.1f avg wpm" % self.average, " - " + (kbd if kbd is not None else "Unspecified"), ) stat = "" for part in parts: if (len(stat) + len(part)) <= self.screen.columns: stat += part return stat def reset(self, direction=0): """Cancels current game.""" self.start = None self.stop = None self.position = 0 self.incorrect = 0 self.total_incorrect = 0 self.cheight = 0 self._edit = "" self.screen.first_key = True self.screen.clear_prompt() if direction: if direction > 0: self.set_quote(self.quotes.next()) else: self.set_quote(self.quotes.previous()) self.screen.clear() def resize(self): """Handles a resized terminal.""" max_y, max_x = self.screen.window.getmaxyx() self.screen.clear() # Check if we have the resizeterm ncurses extension if hasattr(curses, "resizeterm"): curses.resizeterm(max_y, max_x) # An ungetch for KEY_RESIZE will be sent to let others handle it. # We'll just pop it off again to prevent endless loops. self.screen.get_key() self.screen.set_quote(self.quote) if self.start is not None and self.stop is None: # Resize during typing requires redrawing quote. self.screen.update_quote(Screen.COLOR_QUOTE) self.screen.update_author() if self.position + self.incorrect <= len(self.quote.text): for pos in range(self.position + 1): self.screen.highlight_progress(pos, 0) for inc in range(self.incorrect + 1): self.screen.highlight_progress(self.position, inc) def handle_key(self, key): """Dispatches actions based on key and current mode.""" # TODO: Refactor this mess of a function if key is None: return if key == "KEY_RESIZE": self.resize() return # Browse mode if self.start is None or (self.start is not None and self.stop is not None): if key in (" ", "KEY_LEFT", "KEY_RIGHT"): self.reset(direction=-1 if key == "KEY_LEFT" else 1) return elif Screen.is_escape(key): # Exit program raise KeyboardInterrupt() if Screen.is_escape(key): self.reset() return self.recorder.add(self.elapsed, key, self.position, self.incorrect) if Screen.is_backspace(key): if self.incorrect: self.incorrect -= 1 self._edit = self._edit[:-1] elif self._edit: self.position -= 1 self._edit = self._edit[:-1] return if self.stop is not None: # Use wants to try again immediately after score self.reset() head = self.get_stats(self.elapsed) self.screen.rerender_race(head) self.screen.show_keystroke(head, self.position, self.incorrect, self._edit, key) # Start recording upon first ordinary key press if self.start is None: self.start = time.time() if key == curses.KEY_ENTER: key = "\n" elif key == "\t" and self.tab_spaces is not None: key = " "*self.tab_spaces # Did the user strike the correct key? if self.incorrect == 0 and self.quote.text[self.position] == key: self.position += 1 # Reset edit buffer on a correctly finished word if key == " " or key == "\n": self.screen.clear_prompt() self._edit = "" else: self._edit += key # Finished typing? if self.position == len(self.quote.text): self.mark_finished() elif self.incorrect + self.position < len(self.quote.text): self.incorrect += 1 self.total_incorrect += 1 if key == "\n": key = " " self._edit += key
def handle_key(self, key): """Dispatches actions based on key and current mode.""" # TODO: Refactor this mess of a function if key is None: return if key == "KEY_RESIZE": self.resize() return # Browse mode if self.start is None or (self.start is not None and self.stop is not None): if key in (" ", "KEY_LEFT", "KEY_RIGHT"): self.reset(direction=-1 if key == "KEY_LEFT" else 1) return elif Screen.is_escape(key): # Exit program raise KeyboardInterrupt() if Screen.is_escape(key): self.reset() return self.recorder.add(self.elapsed, key, self.position, self.incorrect) if Screen.is_backspace(key): if self.incorrect: self.incorrect -= 1 self._edit = self._edit[:-1] elif self._edit: self.position -= 1 self._edit = self._edit[:-1] return if self.stop is not None: # Use wants to try again immediately after score self.reset() head = self.get_stats(self.elapsed) self.screen.rerender_race(head) self.screen.show_keystroke(head, self.position, self.incorrect, self._edit, key) # Start recording upon first ordinary key press if self.start is None: self.start = time.time() if key == curses.KEY_ENTER: key = "\n" elif key == "\t" and self.tab_spaces is not None: key = " "*self.tab_spaces # Did the user strike the correct key? if self.incorrect == 0 and self.quote.text[self.position] == key: self.position += 1 # Reset edit buffer on a correctly finished word if key == " " or key == "\n": self.screen.clear_prompt() self._edit = "" else: self._edit += key # Finished typing? if self.position == len(self.quote.text): self.mark_finished() elif self.incorrect + self.position < len(self.quote.text): self.incorrect += 1 self.total_incorrect += 1 if key == "\n": key = " " self._edit += key