def draw(canvas: curses.window): canvas.border() canvas.nodelay(True) # https://docs.python.org/3/library/curses.html#curses.window.getmaxyx window_height, window_width = canvas.getmaxyx() # rows and columns greater by one then real window size max_y, max_x = window_height - 1, window_width - 1 curses.curs_set(False) # Reducing max dimensions by 2 allows to avoid "curses.error" stars_max_y = window_height - 2 stars_max_x = window_width - 2 coroutines = [ blink( canvas, random.randint(1, stars_max_y), # stars mustn't appear on border random.randint(0, stars_max_x), delay=random.randint(1, 20), ) for _ in range(random.randint(100, 200)) ] coroutines.append(animate_rocket_flight(canvas, max_y, max_x)) while coroutines: for coro in coroutines.copy(): try: coro.send(None) except StopIteration: coroutines.remove(coro) canvas.refresh() time.sleep(TIC_TIMEOUT)
def display_menu(screen: curses.window, menu_items, cur_index: int): """ prints the menu to the console """ # obtaining size of the screen sheight, swidth = screen.getmaxyx() welcome = "Welcome to Battleships" screen.addstr(1, swidth // 2 - len(welcome) // 2, '#' * len(welcome)) screen.addstr(2, swidth // 2 - len(welcome) // 2, welcome) screen.addstr(3, swidth // 2 - len(welcome) // 2, '#' * len(welcome)) # printing the menu in center of the screen for index, item in enumerate(menu_items): x = swidth // 2 - len(item) // 2 y = sheight // 2 - len(menu_items) // 2 + index if index == cur_index: screen.attron(curses.color_pair(1)) screen.addstr(y, x, item) screen.attroff(curses.color_pair(1)) else: screen.addstr(y, x, item) screen.refresh()
def curses_program(screen: curses.window): y, x = screen.getmaxyx() o = Ocean(x - 1, y - 1) screen.clear() curses.resizeterm(y, x) for instruct in data: screen.clear() curr_north, curr_east = c.north, c.east c.execute_instruction(instruct) new_north, new_east = c.north, c.east if new_north < curr_north: for i in range(abs(curr_north - new_north)): o.move_ocean_south() o.draw(screen) else: for i in range(abs(curr_north - new_north)): o.move_ocean_north() o.draw(screen) if new_east < curr_east: for i in range(abs(curr_east - new_east)): o.move_ocean_west() o.draw(screen) else: for i in range(abs(curr_east - new_east)): o.move_ocean_east() o.draw(screen)
def __init__(self, _x, _y, _window: curses.window, _orientation: int = 0, _arr: Union[None, list] = None, _score=4): """ класс фигуры ## ## :param _x: :param _y: :param _window: window_magic_creater :param _orientation: :param _arr: :param _score: """ self.x = _x self.y = _y self.window = _window self.max_x = _window.getmaxyx()[1] - 2 self.max_y = _window.getmaxyx()[0] - 2 self.exist = True self.arr = _arr self.score = _score self.orientation = _orientation
def draw_frame(canvas: curses.window, start_row: int, start_column: int, text: str, negative: bool = False): """Draw multiline text fragment on canvas. Erase text instead of drawing if negative=True is specified.""" rows_number, columns_number = canvas.getmaxyx() for row, line in enumerate(text.splitlines(), round(start_row)): if row < 0: continue if row >= rows_number: break for column, symbol in enumerate(line, round(start_column)): if column < 0: continue if column >= columns_number: break if symbol == " ": continue # Check that current position it is not in a lower right corner of the window # Curses will raise exception in that case. Don`t ask why… # https://docs.python.org/3/library/curses.html#curses.window.addch if row == rows_number - 1 and column == columns_number - 1: continue symbol = symbol if not negative else " " canvas.addch(row, column, symbol)
def display_stats(screen: curses.window, statistics, my: bool, game: Game): """ function for printing a table displaying how many ships are alive/dead """ # if the size of the longest ship is greater than 50 # then tables with stats won't fit on the screen # thus, just don't print it if game.longest_ship_sz > 50: return sheight, swidth = screen.getmaxyx() title = "Your Ships" if my else "Enemy Ships" str_size = "Size" str_alive = "Alive" str_dead = "Dead" col_names = f"| {str_size} | {str_alive} | {str_dead} |" offset_x = 0 # Each table is 8 blocks away from fields if my: offset_x = swidth // 2 - Field.WINDOW_WIDTH * len(Field.EMPTY) // 2 \ - len(str(game.map_width)) - len(col_names) - 8 else: offset_x = swidth // 2 - Field.WINDOW_WIDTH * len(Field.EMPTY) // 2 \ + (len(Field.EMPTY) + 1) * Field.WINDOW_WIDTH + 8 # Each table close to the middle of the fields but at least 1 block away from the top of the console offset_y = max(1, (Field.WINDOW_HEIGHT * 2 + 10) // 2 - (len(statistics) + 3) // 2) longest_ship_sz = game.longest_ship_sz # printing table's heading screen.addstr(offset_y, offset_x + (len(col_names) // 2 - len(title) // 2), title) screen.addstr(offset_y + 1, offset_x, "-" * (len(col_names))) screen.addstr(offset_y + 2, offset_x, col_names) # printing stats row = 3 for i in range(len(statistics)): alive = str(statistics[i]) dead = str(longest_ship_sz - (i + 1) + 1 - statistics[i]) # cross out if there are no alive ships if alive == '0': screen.addstr(offset_y + row, offset_x, "|" + "/" * (len(col_names) - 2) + "|") else: screen.addstr( offset_y + row, offset_x, "| " + str(i + 1) + " " * (len(str_size) + 1 - len(str(i + 1))) + '| ' + alive + " " * (len(str_alive) + 1 - len(alive)) + '| ' + dead + " " * (len(str_dead) + 1 - len(dead)) + "|") row += 1 screen.addstr(offset_y + row, offset_x, "-" * (len(col_names)))
def show_sprite(scr: c.window, sprite: list, erase=True): if erase: scr.erase() scr_y, scr_x = scr.getmaxyx() begin_y = scr_y // 2 - len(sprite) // 2 begin_x = scr_x // 2 - len(sprite[0]) // 2 for i, s in enumerate(sprite): scr.addstr(begin_y + i, begin_x, s) scr.refresh()
def print_to_center(screen: curses.window, messages) -> None: """ function for printing messages to the center of the screen :param screen: :param messages: list-like iterable element """ sheight, swidth = screen.getmaxyx() for index, message in enumerate(messages): screen.addstr(sheight // 2 + index, swidth // 2 - len(message) // 2, message)
def main(screen: curses.window, height, width): game: Optional[Game] = None curses.curs_set(0) while True: display_string_builder: list[str, ...] = list() display_string_builder.append('###########################') display_string_builder.append('# WELCOME TO BATTLESHIP #') display_string_builder.append('###########################') display_string_builder.append('') if game and game.winner: display_string_builder.append( f'The winner of the last game is {game.winner}!') display_string_builder.append('') display_string_builder.append('Press n to start new game.') display_string_builder.append('Press l to load the game (if exists).') display_string_builder.append('Press q to quit the game.') screen.clear() check_text_size(display_string_builder, *screen.getmaxyx()) display_string = '\n'.join(display_string_builder) screen.addstr(0, 0, display_string) input_character = screen.getch() if input_character == ord('q'): return if input_character in (ord('n'), ord('l')): if input_character == ord('l'): try: with open(SAVE_FILE_NAME, 'rb') as file: game = pickle.load(file) except OSError: continue else: first_player = HumanPlayer('You') second_player = RandomPlayer('Robot') game = Game(first_player=first_player, second_player=second_player, board_size=(height, width)) try: game.play(screen) except QuitSignal as qs: if qs.signal_type == QuitSignal.BACK_TO_MAIN_MENU: continue if qs.signal_type == QuitSignal.QUIT: return if qs.signal_type == QuitSignal.QUIT_AND_SAVE: with open(SAVE_FILE_NAME, 'wb') as file: pickle.dump(game, file, pickle.HIGHEST_PROTOCOL) return
def display_game(screen: curses.window, cur_player: Player, game: Game): """ function for printing game on the console """ # obtain coordinates for displaying fields sheight, swidth = screen.getmaxyx() my_coords, enemy_coords = game.display_field(sheight=sheight, swidth=swidth) # print fields and stats on the console screen.clear() display_field(screen, my_coords, cur_player, True, game=game) display_field(screen, enemy_coords, cur_player, False, game=game) stats = game.num_alive_ships() display_stats(screen=screen, statistics=stats[0], my=True, game=game) display_stats(screen=screen, statistics=stats[1], my=False, game=game)
def run_game(scr: curses.window): curses.curs_set(0) scr.nodelay(True) game_state = "" status_w: int = 25 maxy = scr.getmaxyx()[0] - 1 maxx = scr.getmaxyx()[1] midy = int(maxy / 2) midx = int(maxx / 2) snake: list = [] for i in range(SNAKE_LEN): snake.append((midy, status_w + midx + i)) direction = WEST apples: list = [] score = 0 while True: scr.erase() draw_statusbar(scr, (maxy - 1, maxx), status_w, score) draw_snek(scr, snake, direction) draw_apples(scr, apples) # ++ Tick ++ key = scr.getch() direction, game_state = process_key(key, direction) snake = move_snake(snake, direction) snake = portals(snake, direction, status_w + 2, (maxy - 1, maxx)) snake, apples, nscore = eat_apples(snake, apples) apples = create_apples(snake, apples, MAX_APPLES - len(apples), status_w, (maxy, maxx)) score += nscore if game_state != "": return game_state, 0 # -- Tick -- scr.refresh() if check_loss(snake): curses.napms(1000) return "end", score curses.napms(100)
def main(stdscr: curses.window) -> None: log.debug('Program starting.') command_args = get_args() screen_height, screen_width = stdscr.getmaxyx() config = configure(screen_height, screen_width, command_args) curses.curs_set(0) init_color_pairs() kb_pad = Pad(1, 1) map_pad = Pad(config.map_pad_h, config.map_pad_w) actor_pad = Pad(1, 1 + (1 if config.square_tiles else 0)) status_bar_pad = Pad(1, config.status_bar_width) map = MapController(map_pad, config) actor = ActorController(actor_pad, config) status_bar = StatusBarController(status_bar_pad, config) pad_controllers = [map, actor, status_bar] runner = ApplicationRunner(kb_pad, pad_controllers, config) runner.run()
def main(scr: curses.window): mode = "movement" for n, pair in enumerate(colors): curses.init_pair(n + 1, *pair) y, x = 0, 0 while True: # maxy/x maxy, maxx = [n - 1 for n in scr.getmaxyx()] maxy -= 1 # output mode scr.move(maxy + 1, 1) scr.clrtobot() scr.addstr(maxy + 1, 1, mode) # move cursor to proper position scr.move(y, x) # get user input key = scr.getch() # try: y, x, scr, mode = modes[mode](key, mode, y, x, maxy, maxx, scr)
def _main(stdscr: curses.window): maxy, maxx = 0, 0 examples = [] count = 1 while 1: # Prompt maxy, maxx = stdscr.getmaxyx() stdscr.clear() stdscr.addstr( 0, 0, "Enter example: (hit Ctrl-G to execute, Ctrl-C to exit)", curses.A_BOLD) editwin = curses.newwin(5, maxx - 4, 2, 2) rectangle(stdscr, 1, 1, 1 + 5 + 1, maxx - 2) # Printing is part of the prompt cur = 8 def putstr(str, x=0, attr=0): nonlocal cur # This is how we handle going off the bottom of the scren lol if cur < maxy: stdscr.addstr(cur, x, str, attr) cur += (len(str.split("\n")) or 1) for ex, buff, vals, err in reversed(examples): putstr(f"Example {ex}:", attr=curses.A_BOLD) for l in buff.split("\n"): putstr(f" | {l}") putstr("") if err: err = str(err) err = err.split("\n") putstr(" Error:") for l in err: putstr(f" {l}", attr=curses.COLOR_YELLOW) elif vals: putstr(" Value:") for x, t in zip(range(1, 1 << 64), vals): putstr(f" {x:>3}) " + repr(t)) putstr("") stdscr.refresh() # Readf rom the user box = Textbox(editwin) try: box.edit() except KeyboardInterrupt: break buff = box.gather().strip() if not buff: continue vals, err = handle(buff, count) examples.append((count, buff, vals, err)) count += 1 stdscr.refresh()
def main(scr: curses.window): # Remove blinking cursor curses.curs_set(0) # Get screen's height and width & check if the screen is big enough sh, sw = scr.getmaxyx() if sh < SCR_H + 2 or sw < SCR_W + 2: show_msg(scr, sh, sw, MSG_SCR_SMALL) # Get args mode, ip, port, plname = get_args(scr, sh, sw) # Start socket for host/join mode skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM) skt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if mode == 'host': try: skt.bind((ip, port)) skt.listen(1) except: show_msg(scr, sh, sw, MSG_CANT_HOST) else: try: skt.connect((ip, port)) except: show_msg(scr, sh, sw, MSG_CANT_JOIN) # Setup keys up_key = set((curses.KEY_UP, ord('k'), ord('K'), ord('w'), ord('W'))) down_key = set((curses.KEY_DOWN, ord('j'), ord('J'), ord('s'), ord('S'))) quit_key = set((ord('q'), ord('Q'))) keys = {'up_key': up_key, 'down_key': down_key, 'quit_key': quit_key} # Activate nodelay (so getch won't interrupt the execution) scr.nodelay(1) scr.timeout(33) # Create arena arena = Arena(0, 1, SCR_W, SCR_H) # Create players player1 = Player('left', arena) player2 = Player('right', arena) # Create the ball ball = Ball(arena.bound_x // 2, arena.bound_y // 2, choice((-1, 1)), choice((-1, 0, 1))) # Connection accepted accepted = False # Waiting connection message scr.addstr(sh // 2, sw // 2 - len(MSG_WAITING) // 2, MSG_WAITING) scr.refresh() scr.addstr(sh // 2, 0, " " * sw) # Draw the arena arena.draw(scr) # Game loop while True: # Start networking if mode == 'host': if not accepted: # Accept client try: clskt, claddr = skt.accept() except: sys.exit() # Write host name on the screen and send it scr.addstr(0, 0, plname) clskt.send(plname.encode().ljust(16)) # Receive client name and add to screen try: clname = clskt.recv(16).strip().decode()[:16] except: show_msg(scr, 0, SCR_W, MSG_DISCONN) scr.addstr(0, SCR_W + 1 - len(clname), clname) # Mark client as accpeted accepted = True else: if not accepted: # Receive host name and add to screen try: scr.addstr(0, 0, skt.recv(16).strip().decode()[:16]) except: show_msg(scr, 0, SCR_W, MSG_DISCONN) # Write client name on the screen and send it scr.addstr(0, SCR_W + 1 - len(plname), plname) skt.send(plname.encode().ljust(16)) accepted = True # Draw the game score scr.addstr(0, SCR_W // 2 - 6, str(player1.score)) scr.addstr(0, SCR_W // 2 + 6, str(player2.score)) # Draw players player1.draw(scr, arena) player2.draw(scr, arena) # Draw ball (host) and check goals if mode == 'host': ball.move(player1, player2, arena) ball.draw(scr) # Get button press, perform action and send over the network if mode == 'host': action = get_action(scr, arena, player1, keys, plname == 'AI', { 'p1': player1.y, 'p2': player2.y, 'ball': ball.upload() }) if action == 'up' and player1.y > arena.y + 3: player1.move('up') elif action == 'down' and player1.y < arena.bound_y - 3: player1.move('down') elif action == 'quit': clskt.close() sys.exit(0) else: action = None # Send ball and host's action try: clskt.send(pickle.dumps((str(action), ball.upload()))) player2_action = clskt.recv(16).strip().decode() if player2_action == 'up' and player2.y > arena.y + 3: player2.move('up') elif player2_action == 'down' and player2.y < arena.bound_y - 3: player2.move('down') except: show_msg(scr, 0, SCR_W, MSG_DISCONN) else: action = get_action(scr, arena, player2, keys, plname == 'AI', { 'p1': player1.y, 'p2': player2.y, 'ball': ball.upload() }) if action == 'up' and player2.y > arena.y + 3: player2.move('up') elif action == 'down' and player2.y < arena.bound_y - 3: player2.move('down') elif action == 'quit': skt.close() sys.exit(0) else: action = None # Send client's action, then get ball and host's position try: skt.send(str(action).encode().ljust(16)) player1_action, ball_info = pickle.loads( skt.recv(sys.getsizeof(('down', ball.upload())) * 3)) if player1_action == 'up' and player1.y > arena.y + 3: player1.move('up') elif player1_action == 'down' and player1.y < arena.bound_y - 3: player1.move('down') ball.download(ball_info) except: show_msg(scr, 0, SCR_W, MSG_DISCONN) # Draw ball (join) and check goals ball.move(player1, player2, arena) ball.draw(scr) scr.refresh()
def collect(cls, window: curses.window) -> State: bounds = Bounds(*window.getmaxyx()) player = Player(bounds=bounds) coins = {Coin.spawn(bounds) for __ in range(5)} return State(bounds=bounds, player=player, coins=coins)
def _debug(scr: curses.window, *args): sargs = str([str(arg) for arg in args])[1:-1] scr.addstr(scr.getmaxyx()[0]-1, 1, sargs)
def center(screen: curses.window) -> Tuple[int, int]: h, w = screen.getmaxyx() return h // 2, w // 2