def paint_infobox(rows, columns, matches, funcprops, arg_pos, match, docstring, config, format): """Returns painted completions, funcprops, match, docstring etc.""" if not (rows and columns): return fsarray(0, 0) width = columns - 4 lines = ( (formatted_argspec(funcprops, arg_pos, width, config) if funcprops else []) + (matches_lines(rows, width, matches, match, config, format) if matches else []) + (formatted_docstring(docstring, width, config) if docstring else [])) def add_border(line): """Add colored borders left and right to a line.""" new_line = border_color(config.left_border + ' ') new_line += line.ljust(width)[:width] new_line += border_color(' ' + config.right_border) return new_line border_color = func_for_letter(config.color_scheme['main']) top_line = border_color(config.left_top_corner + config.top_border * (width + 2) + config.right_top_corner) bottom_line = border_color(config.left_bottom_corner + config.bottom_border * (width + 2) + config.right_bottom_corner) output_lines = list( itertools.chain((top_line, ), map(add_border, lines), (bottom_line, ))) r = fsarray(output_lines[:min(rows - 1, len(output_lines) - 1)] + output_lines[-1:]) return r
def paint_infobox(rows, columns, matches, argspec, match, docstring, config, format): """Returns painted completions, argspec, match, docstring etc.""" if not (rows and columns): return fsarray(0, 0) width = columns - 4 lines = ((formatted_argspec(argspec, width, config) if argspec else []) + (matches_lines(rows, width, matches, match, config, format) if matches else []) + (formatted_docstring(docstring, width, config) if docstring else [])) def add_border(line): """Add colored borders left and right to a line.""" new_line = border_color(config.left_border + ' ') new_line += line.ljust(width)[:width] new_line += border_color(' ' + config.right_border) return new_line border_color = func_for_letter(config.color_scheme['main']) top_line = border_color(config.left_top_corner + config.top_border * (width + 2) + config.right_top_corner) bottom_line = border_color(config.left_bottom_corner + config.bottom_border * (width + 2) + config.right_bottom_corner) output_lines = list(itertools.chain((top_line, ), map(add_border, lines), (bottom_line, ))) r = fsarray(output_lines[:min(rows - 1, len(output_lines) - 1)] + output_lines[-1:]) return r
def paint_last_events(rows, columns, names, config): if not names: return fsarray([]) width = min(max([len(name) for name in names] + [0]), columns-2) output_lines = [] output_lines.append(config.left_top_corner+config.top_border*(width)+config.right_top_corner) for name in reversed(names[max(0, len(names)-(rows-2)):]): output_lines.append(config.left_border+name[:width].center(width)+config.right_border) output_lines.append(config.left_bottom_corner+config.bottom_border*width+config.right_bottom_corner) return fsarray(output_lines)
def paint_last_events(rows, columns, names): if not names: return fsarray([]) width = min(max(len(name) for name in names), columns - 2) output_lines = [] output_lines.append(u'┌' + u'─' * width + u'┐') for name in names[-(rows - 2):]: output_lines.append(u'│' + name[:width].center(width) + u'│') output_lines.append(u'└' + u'─' * width + u'┘') return fsarray(output_lines)
def paint_last_events(rows, columns, names): if not names: return fsarray([]) width = min(max(len(name) for name in names), columns-2) output_lines = [] output_lines.append(u'┌'+u'─'*width+u'┐') for name in names[-(rows-2):]: output_lines.append(u'│'+name[:width].center(width)+u'│') output_lines.append(u'└'+u'─'*width+u'┘') return fsarray(output_lines)
def paint_last_events(rows, columns, names, config): if not names: return fsarray([]) width = min(max(len(name) for name in names), columns - 2) output_lines = [] output_lines.append(config.left_top_corner + config.top_border * width + config.right_top_corner) for name in reversed(names[max(0, len(names) - (rows - 2)) :]): output_lines.append(config.left_border + name[:width].center(width) + config.right_border) output_lines.append(config.left_bottom_corner + config.bottom_border * width + config.right_bottom_corner) return fsarray(output_lines)
def produce_box_of_size_height_width(height, width): """ Give it height and width, and it'll give you a bordered box as a FSArray. """ top_left = '╒' top_right = '╕' bottom_left = '╘' bottom_right = '╛' horizontal = '═' vertical = '│' box_array = fsarray([' ' * width for _ in range(height)]) box_array[0, 0] = top_left box_array[0, width - 1] = top_right box_array[height - 1, 0] = bottom_left box_array[height - 1, width - 1] = bottom_right if width >= 3: # fill space between corners if needed span = width - 2 for index in range(span): box_array[0, index + 1] = horizontal box_array[height - 1, index + 1] = horizontal if height >= 3: span = height - 2 for index in range(span): box_array[index + 1, 0] = vertical box_array[index + 1, width - 1] = vertical return box_array
def draw_menu(self, title: FmtStr, items: List[FmtStr]) -> None: """Draws the menu of the specified items""" height = len(items) width = max(( title.width, max(item.width for item in items) if items else 0, )) header = [ "╭" + "-" * (width + 2) + "╮", "|" + title.center(width + 2) + "|", "|" + "-" * (width + 2) + "|", ] footer = [ "╰" + "-" * (width + 2) + "╯", ] body = fsarray(header + ["| " + i.ljust(width) + " |" for i in items] + footer) rows = len(body) cols = max(row.width for row in body) min_row = int(self.max_rows / 2 - rows / 2) min_col = int(self.max_cols / 2 - cols / 2) self.chars[min_row:min_row + rows, min_col:min_col + cols] = body
def yields_FSArray_boxes(height, width, max=15): top_left = '╒' top_right = '╕' bottom_left = '╘' bottom_right = '╛' horizontal = '═' vertical = '│' for i in range(max): if height: box_array = fsarray([' ' * width for _ in range(height)]) # FSArrays are height,width <- weird but true box_array[0,0] = top_left box_array[0,width-1] = top_right box_array[height-1,0] = bottom_left box_array[height-1,width-1] = bottom_right if width>2: span = width-2 for index in range(span): box_array[0,index+1] = horizontal box_array[height-1, index + 1] = horizontal if height>2: span = height-2 for index in range(span): box_array[index+1,0] = vertical box_array[index+1,width-1] = vertical height = yield box_array
def yields_FSArray_boxes(height = 10, width = 10, max=15): top_left = '╒' top_right = '╕' bottom_left = '╘' bottom_right = '╛' horizontal = '═' vertical = '│' i = 0 while i < max: box_array = fsarray([' ' * width for _ in range(height)]) # FSArrays are height,width <- weird but true box_array[0,0] = top_left box_array[0,width-1] = top_right box_array[height-1,0] = bottom_left box_array[height-1,width-1] = bottom_right if width>2: span = width-2 for index in range(span): box_array[0,index+1] = horizontal box_array[height-1, index + 1] = horizontal if height>2: span = height-2 for index in range(span): box_array[index+1,0] = vertical box_array[index+1,width-1] = vertical yielded = yield box_array if yielded: # handled case when the yield brings back a NoneType height, width = yielded[0], yielded[1] i += 1
def main_loop(self, bookmarks): with Window(out_stream=sys.stderr, hide_cursor=True) as window: clean_display = fsarray([self.bg(' ')*window.width for _ in range(window.height)]) display = fsarray(clean_display) self.__update_screen(display) window.render_to_terminal(display) done = False; while not done: with Input(keynames='curtsies') as _: for e in Input(): display = fsarray(clean_display) done = self.__process_key(e) self.__update_screen(display) window.render_to_terminal(display) if done: break
def paint_history(rows, columns, display_lines): lines = [] for r, line in zip(range(rows), display_lines[-rows:]): lines.append(fmtstr(line[:columns])) r = fsarray(lines, width=columns) assert r.shape[0] <= rows, repr(r.shape) + " " + repr(rows) assert r.shape[1] <= columns, repr(r.shape) + " " + repr(columns) return r
def render(self, width): arr = fsarray('', width=width) if self._prompt: arr.rows = self._prompt.rows + arr.rows l = len(arr) for checked, option in self._choices: current = self._choices[self._idx][1] == option fmt = self._sel_fmt if current else self._des_fmt opt_arr = option.render(fmt, width-3) arr[l:l+len(opt_arr), 2:width] = opt_arr if self._multi: state = self._sel if checked else self._des else: state = '> ' if current else ' ' arr[l:l+1, 0:2] = fsarray([state]) l += len(opt_arr) return arr
def paint_history(rows, columns, display_lines): lines = [] for r, line in zip(range(rows), display_lines[-rows:]): lines.append(fmtstr(line[:columns])) r = fsarray(lines, width=columns) assert r.shape[0] <= rows, repr(r.shape) + ' ' + repr(rows) assert r.shape[1] <= columns, repr(r.shape) + ' ' + repr(columns) return r
def paint_infobox(rows, columns, matches, argspec, match, docstring, config, format): """Returns painted completions, argspec, match, docstring etc.""" if not (rows and columns): return fsarray(0, 0) width = columns - 4 lines = ((formatted_argspec(argspec, width, config) if argspec else []) + (matches_lines(rows, width, matches, match, config, format) if matches else []) + (formatted_docstring(docstring, width, config) if docstring else [])) output_lines = [] border_color = func_for_letter(config.color_scheme['main']) output_lines.append(border_color(u'┌─'+u'─'*width+u'─┐')) for line in lines: output_lines.append(border_color(u'│ ')+((line+' '*(width - len(line)))[:width])+border_color(u' │')) output_lines.append(border_color(u'└─'+u'─'*width+u'─┘')) r = fsarray(output_lines[:min(rows-1, len(output_lines)-1)] + output_lines[-1:]) return r
def confirm(prompt, true='yes', false='no', *, default=False, single_key=False, true_key='y', false_key='n', clear=True): with CursorAwareWindow(out_stream=sys.stderr, extra_bytes_callback=lambda x: x, keep_last_line=not clear) as window: prompt = prompt + _keys(true_key, false_key, default) width = min(min(window.width, 80) - len(true + false) - 5, len(prompt)) prompt_arr = fsarray( (bold(line) for line in textwrap.wrap(prompt, width=width)), width=window.width) choice = fsarray([' '.join((true, false))]) window.render_to_terminal(prompt_arr) selected = None with Input() as keyGen: for i in keyGen: try: if i == true_key: selected = True if single_key: break elif i == false_key: selected = False if single_key: break elif i in ('<LEFT>', '<UP>', '<DOWN>', '<RIGHT>'): selected = not selected elif i == '<Ctrl-j>': if selected is None: selected = default break finally: if (selected is not None): choice = fsarray([true if selected else false]) prompt_arr[0:1, width + 1:width + len(true + false) + 5] = choice window.render_to_terminal(prompt_arr) return selected
def draw_exs(grid, coord): """ Build an `X` `FSArray` and attach to the base grid """ ex = block('`', 5, 6) ex[0:1, :] = fsarray(['\ /']) ex[1:2, :] = fsarray([' \ / ']) ex[2:3, :] = fsarray([' \/ ']) ex[3:4, :] = fsarray([' /\ ']) # Strangely this last backspace was treated as an escape character by # the interpreter ex[4:5, :] = fsarray([' / \\ ']) height = coord[0] width = coord[1] + 1 # place block, block.height*row from top and block.width*column from right grid[height:height + ex.height, width:width + ex.width] = ex return grid
def string_as_array_of_width(string, width): """take a string, and break it into a list of strings, each of maximum length width, return that list as an FSArray""" lines = [''] line_number = 0 for i, character in enumerate(string): lines[line_number] = lines[line_number] + character if i > 2 and i % width == 0: line_number += 1 lines.append('') return fsarray(lines)
def render(self) -> None: """representation of the field state""" rows = [] for row in range(self.height): row_str = self.char_at((row, 0)) for col in range(1, self.width): row_str = row_str.append(self.char_at((row, col))) rows.append(row_str) self.chars = fsarray(rows) self.highlighted = set()
def paint_infobox(rows, columns, matches, argspec, match, docstring, config, format): """Returns painted completions, argspec, match, docstring etc.""" if not (rows and columns): return fsarray(0, 0) width = columns - 4 lines = ( (formatted_argspec(argspec, width, config) if argspec else []) + (matches_lines(rows, width, matches, match, config, format) if matches else []) + (formatted_docstring(docstring, width, config) if docstring else [])) output_lines = [] border_color = func_for_letter(config.color_scheme['main']) output_lines.append(border_color(u'┌─' + u'─' * width + u'─┐')) for line in lines: output_lines.append( border_color(u'│ ') + ((line + ' ' * (width - len(line)))[:width]) + border_color(u' │')) output_lines.append(border_color(u'└─' + u'─' * width + u'─┘')) r = fsarray(output_lines[:min(rows - 1, len(output_lines) - 1)] + output_lines[-1:]) return r
def __init__(self, choices, prompt=None, multi=False, sel_fmt=bold, des_fmt=_no_fmt, selected=CHECKED, deselected=UNCHECKED): if prompt: self._prompt = fsarray([bold(line) for line in prompt.split('\n')]) else: self._prompt = prompt if multi is True: multi = (0, len(choices)) self._multi = multi if not choices: raise ValueError('No choices given') self._choices = [[False, Choice(c)] for c in choices] self._sel_fmt = sel_fmt self._des_fmt = des_fmt self._sel = selected self._des = deselected self._idx = 0
def __init__(self): self.width = 10 self.height = 10 self.mine_count = 10 # game clock tracking self.started: Optional[pendulum.DateTime] = None self.ended: Optional[pendulum.DateTime] = None self.stoppage_time: pendulum.Duration = pendulum.duration() # initialize cursor position self.chars: FSArray = fsarray([]) self.row, self.col = 5, 5 self.mines: Set[Tuple[int, int]] = self.init_mines() self.flagged: Set[Tuple[int, int]] = set() self.opened: Set[Tuple[int, int]] = set() self.highlighted: Set[Tuple[int, int]] = set() self.render()
def box_handler(height=10, width=20, contents=''): """ A coroutine style generator for creating boxes with borders and contents """ cursor = [0, 0] # used to remember the position in a content_array box box_updated = True content_string = contents content_array = fsarray('') while True: if box_updated: box_array = produce_box_of_size_height_width(height, width) box_array[1:content_array.height + 1, 1: content_array.width + 1] = content_array box_updated = False yielded = yield box_array if yielded and isinstance(yielded, str): if yielded == 'q': raise StopIteration elif len(yielded) == 1 or yielded == '<SPACE>': if yielded == '<SPACE>': yielded = ' ' content_string += yielded content_array = string_as_array_of_width(content_string, width - 3) # width-3 because # 2 columns get used up on the frame if cursor[1] >= width - 2: cursor[1] = 1 cursor[0] += 1 else: cursor[1] += 1 # This is how curtsies represents arrow keys: elif yielded in ('<UP>', '<DOWN>', '<RIGHT>', '<LEFT>'): if yielded == '<UP>': height += 1 elif yielded == '<DOWN>' and height >= 3: # don't let the box poof it self height += -1 elif yielded == '<LEFT>' and width >= 3: width += -1 elif yielded == '<RIGHT>': width += 1 else: pass box_updated = True
def _string_to_fsarray(self, msg): if not self._inside_window_context: raise RuntimeError( 'Calling _string_to_fsarray outside of window context') rows, columns = self._window_context.get_term_hw() msg = fmtstr(msg) arr = FSArray(0, columns) i = 0 lines = msg.split('\n') if '\n' in str(msg) else [msg] for line in lines: for j in xrange(len(line)): c = line[j] if i >= rows * columns: return arr else: arr[i // arr.width, i % arr.width] = [c] i += 1 i = ((i // columns) + 1) * columns if len(arr) == 0: return fsarray(['']) return arr
def display(grid=None, stop=False, pause=0): if not grid: return m = ["-" * 30] for r in grid: mm = "|" for e in r: mm = mm + ' ' + (str(e) if e else ' ') + ' ' mm += '|' m.append(mm) m.append("-" * 30) if stop: m.append("Hit any key to see more") with Input() as input: with FullscreenWindow() as win: win.render_to_terminal(fsarray(m)) if stop: c = input.next() if not pause: time.sleep(pause)
def do_introduction(window): h, w = window.height, window.width messages = [ "two player tron", fmtstr("player 1:", "on_blue", "cyan") + " wasd", fmtstr("player 2:", "on_red", "yellow") + " arrow keys", ] billboard = FSArray(h, w) msg_row = h // 2 - 2 for msg in messages: billboard[msg_row, w // 2 - len(msg) // 2:w // 2 + len(msg) // 2 + 1] = fsarray([msg]) msg_row += 1 window.render_to_terminal(billboard) # countdown msg for i in range(3, 0, -1): billboard[msg_row, w // 2] = fmtstr(str(i), "red") window.render_to_terminal(billboard) time.sleep(1)
# https://github.com/thomasballinger/curtsies from curtsies.fmtfuncs import * from curtsies import FullscreenWindow, Input, fsarray # and a little standard library! import sys from collections import OrderedDict from itertools import permutations import copy coordinates = OrderedDict() TURN = 'O' ROUND = 0 BOARD = {} oh_turn = fsarray(["""It's OH's turn"""]) ex_turn = fsarray(["""It's EX's turn"""]) def base(): """ Renders a large base/background grid """ return fsarray([x * (7 * 8) for x in 'X' * (5 * 7)]) def block(char, height, width): """ Clears space for O/X in the board background """ return fsarray([x * width for x in char * height])
def paint_current_line(rows, columns, current_display_line): lines = display_linize(current_display_line, columns, True) return fsarray(lines, width=columns)
def scan_controls(camera, menu): global imgcount global hdrframe global exprange global energize global forward global scanning menu.pause() delay = mindelay stopscan = th.Event() stopscan.clear() izero = 5 iss = 3 + izero isd = 5 + izero isc = 7 + izero isr = 9 + izero iim = 11 + izero ihd = 13 + izero with ci.FullscreenWindow() as window: with ci.Input() as inputgen: scr = ci.FSArray(window.height, window.width) ilast = window.height scr[izero - 1, 0:window.width - 1] = ci.fsarray([u'_' * window.width]) scr[ilast - 1, 0:window.width - 2] = ci.fsarray([u'_' * window.width]) scr[ilast - 2, 0:window.width - 2] = ci.fsarray([u'_' * window.width]) msg = ci.fmtstr(on_blue(bold(yellow(u'CONTROL INTERFACE')))) center = int((window.width - msg.width) / 2) scr[izero, center:msg.width] = [msg] msgspeed = ci.fmtstr(u'delay: ') scr[iss, 0:msgspeed.width] = [msgspeed] ispeed = msgspeed.width + 1 msgcw = ci.fmtstr(u'direction:') scr[isd, 0:msgcw.width] = [msgcw] icw = msgcw.width + 2 msgfwd = ci.fmtstr('FORWARD ') msgback = ci.fmtstr('BACKWARD') msgamp = ci.fmtstr(u'position:') scr[isc, 0:msgamp.width] = [msgamp] msgampon = ci.fmtstr(bold(green('LOCKED '))) msgampoff = ci.fmtstr(bold(yellow('UNLOCKED'))) msgrun = ci.fmtstr(u'state:') scr[isr, 0:msgrun.width] = [msgrun] msgon = ci.fmtstr(bold(green('SCANNING'))) msgoff = ci.fmtstr(bold(red('STOP '))) msgimg = ci.fmtstr(u'imgcount: ') scr[iim, 0:msgimg.width] = [msgimg] imgstg = ci.fmtstr(str(imgcount).zfill(imglgth)) scr[iim, icw:icw + imgstg.width] = [imgstg] delaylab = ci.fmtstr(on_blue(bold(yellow('delay (s) =')))) delaystg = ci.fmtstr(on_blue(red(bold(' ' + str(int(delay)))))) scr[izero, 0:delaylab.width] = [delaylab] scr[izero, delaylab.width:delaylab.width + delaystg.width + 1] = [delaystg] isolab = ci.fmtstr(on_blue(bold(yellow('iso =')))) if camera.error == 0: isostg = ci.fmtstr( on_blue(red(bold(' ' + str(camera.get_iso()))))) else: isostg = ci.fmtstr(on_blue(red(bold(' ' + 'No Cam')))) scr[izero, window.width - isolab.width - isostg.width:window.width - isostg.width] = [isolab] scr[izero, window.width - isostg.width:window.width] = [isostg] shutlab = ci.fmtstr(on_blue(bold(yellow('exptime =')))) if camera.error == 0: shutstg = ci.fmtstr( on_blue(red(bold(' ' + str(camera.get_exposure_time()))))) else: shutstg = ci.fmtstr(on_blue(red(bold(' ' + 'No Cam')))) icenter = int((window.width + shutlab.width + shutstg.width) / 2) scr[ilast - 2, icenter - shutlab.width - shutstg.width:icenter - shutstg.width] = [shutlab] scr[ilast - 2, icenter - shutstg.width:icenter] = [shutstg] hdrlab = ci.fmtstr(on_blue(bold(yellow('hdrframe =')))) hdrstg = ci.fmtstr(on_blue(red(bold(' ' + str(hdrframe))))) scr[ilast - 2, window.width - hdrlab.width - hdrstg.width:window.width - hdrstg.width] = [hdrlab] scr[ilast - 2, window.width - hdrstg.width:window.width] = [hdrstg] explab = ci.fmtstr(on_blue(bold(yellow('exprange =')))) expstg = ci.fmtstr(on_blue(red(bold(' ' + str(exprange))))) scr[ilast - 2, 0:explab.width] = [explab] scr[ilast - 2, explab.width:explab.width + expstg.width + 1] = [expstg] scanning = False if not scanning: scr[isr, icw:icw + msgoff.width] = [msgoff] else: scr[isr, icw:icw + msgon.width] = [msgon] if forward: scr[isd, icw:icw + msgfwd.width] = [msgfwd] else: scr[isd, icw:icw + msgback.width] = [msgback] if energize: scr[isc, icw:icw + msgampon.width] = [msgampon] else: scr[isc, icw:icw + msgampoff.width] = [msgampoff] #displays initial values window.render_to_terminal(scr) for c in inputgen: if c == '<ESC>': if scanning: stopscan.set() thscan.join(timeout=None) scanning = False break elif c == '<UP>': ispeed = max(ispeed + 1, msgspeed.width + 1) ispeed = min(ispeed, window.width - 1) scr[iss, ispeed:ispeed + 1] = [ci.fmtstr(yellow('|'))] delay = int(mindelay + float(ispeed - msgspeed.width - 1) / float(window.width - msgspeed.width - 2) * (maxdelay - mindelay)) elif c == '<DOWN>': scr[iss, ispeed:ispeed + 1] = [ci.fmtstr(u' ')] ispeed = max(ispeed - 1, msgspeed.width + 1) ispeed = min(ispeed, window.width - 1) delay = int(mindelay + float(ispeed - msgspeed.width - 1) / float(window.width - msgspeed.width - 2) * (maxdelay - mindelay)) elif c == '<RIGHT>': if not scanning: if not forward: forward = True scr[isd, icw:icw + msgfwd.width] = [msgfwd] elif c == '<LEFT>': if not scanning: if forward: forward = False scr[isd, icw:icw + msgback.width] = [msgback] elif c == '<SPACE>': scanning = not (scanning) if scanning: stopscan.clear() thscan = th.Thread(name='scan', target=scan_frames, args=[camera, delay, stopscan]) thscan.start() scr[isr, icw:icw + msgon.width] = [msgon] else: stopscan.set() thscan.join(timeout=None) scr[isr, icw:icw + msgoff.width] = [msgoff] elif c == '<Ctrl-j>': energize = not (energize) if energize: scr[isc, icw:icw + msgampon.width] = [msgampon] else: scr[isc, icw:icw + msgampoff.width] = [msgampoff] else: msghelp = ci.fmtstr( bold( yellow( u'Use enter, arrow keys and space bar for control. Escape to exit' ))) centerhelp = int((window.width - msghelp.width) / 2) scr[ilast - 1, centerhelp:centerhelp + msghelp.width] = [msghelp] #display updated values # delaylab=ci.fmtstr(on_blue(bold(yellow('delay =')))) delaystg = ci.fmtstr(on_blue(red(bold(' ' + str(int(delay)))))) scr[izero, 0:delaylab.width] = [delaylab] scr[izero, delaylab.width:delaylab.width + delaystg.width + 1] = [delaystg] imgstg = ci.fmtstr(str(imgcount).zfill(imglgth)) scr[iim, icw:icw + imgstg.width] = [imgstg] window.render_to_terminal(scr) menu.resume() return
def paint_statusbar(rows, columns, msg, config): func = func_for_letter(config.color_scheme['main']) return fsarray([func(msg.ljust(columns))[:columns]])
def mainloop(window, p2_bot=False): p1_attrs = { "appearance": on_blue(cyan("1")), "x": window.width // 4, "y": window.height // 2, "keys": { "w": 90, "a": 180, "s": 270, "d": 0 }, } p2_attrs = { "appearance": on_red(yellow("2")), "x": 3 * window.width // 4, "y": window.height // 2, "keys": { "<UP>": 90, "<LEFT>": 180, "<DOWN>": 270, "<RIGHT>": 0 }, } FPS = 15 players = [Cycle(p1_attrs), Cycle(p2_attrs)] if p2_bot: # make p2 a bot players[1] = Bot(p2_attrs) world = gameboard(window.width, window.height, players) dt = 1 / FPS world.draw_border() window.render_to_terminal(world.grid) reactor = Input() schedule_next_frame = reactor.scheduled_event_trigger(Frame) schedule_next_frame(when=time.time()) with reactor: for c in reactor: if isinstance(c, Frame): tick = world.tick() window.render_to_terminal(world.grid) if not tick: # if no crashes when = c.when + dt while when < time.time(): when += dt schedule_next_frame(when) else: # if crashed world.grid[0:4, 0:25] = fsarray([ world.winner_msg(tick), "r to restart", "q to quit", "b to make player 2 a bot", ]) window.render_to_terminal(world.grid) elif c.lower() in ["r", "q", "b"]: break else: # common case world.process_event(c) if c.lower() == "r": mainloop(window, p2_bot) elif c.lower() == "b": mainloop(window, True)
def to_fsarray(self, width, prompt, tail=""): """Returns wrapped and coloured output as an fsarray. Includes the given prompt and tail and fits in the given width.""" string = yellow(prompt) + fmtstr("".join(self.typed)) + tail chunks = [string[i:i + width] for i in range(0, len(string), width)] return fsarray(chunks)
def block(char, height, width): """ Clears space for O/X in the board background """ return fsarray([x * width for x in char * height])
def main(): global TURN, ROUND # 1) Start user/keyboard input handler with Input() as input: # 2) Start window/graphics manager with FullscreenWindow() as window: # 3) Build the initial view b = make_grid(block(' ', 5, 8)) half_half = ' ' * int((b.width / 2) / 2) b[0:1, :] = fsarray(['{}PRESS A KEY TO GET STARTED' .format(half_half)]) b[b.height - 1:b.height, :] = fsarray(['{} It is round {}.' .format(half_half, ROUND)]) # 4) Draw initial screen window.render_to_terminal(b) # 5) Enter the main/game loop while True: # In the current implementation, playing beyond round 9 is # fruitless. However, we need a buffer to enable presentation # of the winner. if ROUND > 10: sys.exit() # Check if a player has a winning row if win_condition(log_board_layout()): # Due to the current ordering we need to switch around the players if TURN=='O': TURN="X" else: TURN="O" # Clear the screen and announce winner b = base() b[0:1, :] = fsarray(['{}Player {} has won!.' .format(half_half, TURN)]) window.render_to_terminal(b) import time time.sleep(3) sys.exit() # 6) Game loop pauses on every pass to await the users next input if TURN == 'O': c = input.next() # Exits on Escape key press if c == '' or c == '<ESC>': sys.exit() # Catch numeric input for determination of where # to draw the ex's and oh's # TODO prevent key presses overriding previous choices. elif c in map('{}'.format, range(1, 10)) and ROUND > 0: b[2:3, :] = fsarray(['{}You pressed key {}.' .format(half_half, c)]) coords = list(coordinates.keys())[int(c) - 1] coords = coordinates[coords] # Capture the players choice to help calculation of winning # plays. log_board_layout(int(c), TURN) if TURN == 'O': draw_oh(b, coords) TURN = 'X' else: # draw_exs(b, coords) TURN = 'O' window.render_to_terminal(b) # TODO Don't increment the round if a different key is pressed # else: # continue b[b.height - 1:b.height, :] = fsarray(['{} It is round {}.' .format(half_half, ROUND)]) # Simple turn management using Global variables # TODO move this to a class to better handle state if TURN == 'O': b[0:1, :] = oh_turn b[1:2, :] = fsarray(['{}Press 1-9 to mark your location.' .format(half_half)]) window.render_to_terminal(b) ROUND += 1 continue b[0:1, :] = ex_turn # A small homage to: http://deepmind.com/alpha-go.html b = alpha_go(b, coordinates) ROUND += 1 window.render_to_terminal(b)
def base(): """ Renders a large base/background grid """ return fsarray([x * (7 * 8) for x in 'X' * (5 * 7)])
def paint_statusbar(rows, columns, msg, config): func = func_for_letter(config.color_scheme["main"]) return fsarray([func(msg.ljust(columns))[:columns]])
def render(self, fmt, width): lines = str(self).split('\n') arr = fsarray(fmt(line) for line in lines) return arr
def display(self): colored = {' ':' ', 'x':blue(bold('x')), 'o':red(bold('o'))} s = ('\n'+green('-')*(len(self.columns)*2-1) + '\n').join([green('|').join(colored[mark] for mark in row) for row in self._rows]) a = fsarray([bold(green('enter a number, 0-8' if self.whose_turn == 'x' else 'wait for computer...'))] + s.split('\n')) return a