def get_people(terminal: Terminal): people = [] known_folders = os.listdir(OUTPUT_CSVS_FRIENDS_DIR) try: progress_bar = ProgressBar( 'Loading knowns from .csv files', max=len(known_folders)) for folder in known_folders: knowns_file = '%s/%s/known_people.csv' % ( OUTPUT_CSVS_FRIENDS_DIR, folder) if os.path.exists(knowns_file): with open(knowns_file, 'r') as f: reader = csv.DictReader(f) for individual in reader: people.append(individual) f.close() progress_bar.next() print() with open('%s/known_people.csv' % OUTPUT_CSVS_DIR, 'r') as f: reader = csv.DictReader(f) progress_bar_knowns = ProgressBar( 'Loading knowns from .csv files', max=sum(1 for row in reader) - 1) for individual in reader: people.append(individual) progress_bar_knowns.next() f.close() except FileNotFoundError as ex: terminal.error(str(ex)) return uniquify(people)
class Player: def __init__(self, name, board=None, color=None, wait_time=None): self.term = Terminal() self.name = f'{name} ({self.__class__.__name__})' self.board = board self.color = color self.wins = 0 self.losses = 0 self.ties = 0 def _take_turn(self): if self.board is None: raise NoGameInProgress('No board has been assigned to this player') self.term.move(*Location(19, 0)) print(f"{self.color or ''}{self.name}{self.term.normal}'s turn") cup = self.take_turn() if cup is None: raise Exception('A cup must be returned by the user') return cup def take_turn(self): raise NotImplementedError( 'take_turn must be implemented by subclasses') def assign_board(self, board): self.board = board def game_over(self, result): if result == Result.Win: self.wins += 1 elif result == Result.Loss: self.losses += 1 elif result == Result.Tie: self.ties += 1 else: raise ValueError('Result not recognized. Got {result}') self.board = None @property def games_played(self): return self.wins + self.losses + self.ties @property def is_player1(self): return self.board.player1 == self @property def is_player2(self): return self.board.player2 == self @property def cup_index(self): return (self.board.player_1_cup_index if self.is_player1 else self.board.player_2_cup_index)
def __init__(self, name, board=None, color=None, wait_time=None): self.term = Terminal() self.name = f'{name} ({self.__class__.__name__})' self.board = board self.color = color self.wins = 0 self.losses = 0 self.ties = 0
def __init__(self) -> None: # basic init for pygame: window/screen, blackground, clock and fonts # fonts need to be initialized extra pygame.init() pygame.font.init() pygame.display.set_caption(GAME_NAME) self.window = pygame.display.set_mode( (WINDOW_SIZE_X + TERMINAL_WIDTH, WINDOW_SIZE_Y)) self.background = pygame.Surface( (TILE_HEIGHT * ROWS, TILE_WIDTH * COLS)) self.clock = pygame.time.Clock() self.terminal_font = pygame.font.SysFont(FONT_TYPE, TERMINAL_FONT_SIZE) self.description_font = pygame.font.SysFont(FONT_TYPE, DESCRIPTION_FONT_SIZE) self.game_over_font = pygame.font.SysFont(FONT_TYPE, GAME_OVER_FONT_SIZE) # init chessboard # an array of meeples is created that reflects the default setup # sprites are loaded from board list which are then added to the sprite group, because it is easier to draw them self.board = Chessboard() self.sprites = self.board.loadSprites() self.chess_sprite_list = pygame.sprite.Group() self.chess_sprite_list.add(self.sprites) # tile representation of the chessboard # used to determine tile/meeple on click events self.chessboard_tiles = self.board.loadTiles(self.background) # init terminal # clickable button without UX -> no highlighting are click reaction # terminal window that logs the moves performed by the players # notations for each player are saved to different arrays; if notations exceed 28 lines -> list.pop is used / no scrolling possible # instance variables for a white meeple that a player has selected and the possbile moves for the according meeple self.terminal = Terminal(self.terminal_font) # init black player self.bot = Bot() # draw terminal on the right side of the game window including a "new game" button drawTerminal(self) # conditions for the game loop self.gameRunning = True # condition for gameloop self.playerTurn = True # white starts playing self.selectedField = False # player has selected a field self.check_white = False self.check_black = False self.draw = False self.check_mate_white = False self.check_mate_black = False # init first possible moves for white player # saves the next possibles for each side self.number_of_moves = self.board.getAllMoves("w")
def __init__( self, prompt, choices=None, ): self.term = Terminal() self.prompt = prompt self.choices = list(choices) if choices else None self._inital_location = INITIAL_MENU_LOCATION
def __init__( self, *, player1, player2, side_length=6, initial_seeds=None, player_1_color=None, player_2_color=None, animation_wait=0.1, ): self.term = Terminal() if player_1_color is None: player_1_color = self.term.bold + self.term.red if player_2_color is None: player_2_color = self.term.bold + self.term.blue seed_color = self.term.green index_color = self.term.yellow self.term.clear() self.term.move(Location(5, 5)) self.board = Board( side_length, seed_color=seed_color, index_color=index_color, animation_wait=animation_wait, ) self.player1 = player1 self.player2 = player2 self.board.assign_player(self.player1) self.board.assign_player(self.player2) self.current_player = self.player1 self._players = (self.player1, self.player2) seeds = initial_seeds or self._get_initial_seeds() self.board.initialize_cups(seeds) self.board.clear_board() self.board.display_cups()
def get_connections(terminal: Terminal): people_file = '%s/people.csv' % OUTPUT_CSVS_DIR connections = [] people = [] persons = [] if not os.path.exists(people_file): terminal.info('Generating people file') people = get_people(terminal) save_list_of_dicts(people_file, people, ['id', 'name']) with open(people_file, 'r') as f: terminal.info('Reading file: %s' % people_file) reader = csv.DictReader(f) for row in reader: person = Person(id=row['id'], name=row['name']) persons.append(person) for person in persons: person.get_knowns(0) for con in get_known_connections(person): connections.append(con)
class GetUserInput: def __init__( self, prompt, choices=None, ): self.term = Terminal() self.prompt = prompt self.choices = list(choices) if choices else None self._inital_location = INITIAL_MENU_LOCATION def get_response(self): self.term.clear() self.term.move(self._inital_location) if self.choices: print(self.prompt) for idx, choice in enumerate(self.choices): print(f' ({idx + 1}) {str(choice)}') done = False while not done: if self.choices: user_resp = input(f'Enter [1-{len(self.choices)}]: ') else: user_resp = input(self.prompt) try: val = int(user_resp) if self.choices: choice = self.choices[val - 1] else: choice = val done = True except ValueError: continue return choice
def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) self.setAttribute(Qt.WA_QuitOnClose) self._connection_scanner = ConnectionScanner() self._connection = None self._root_dir = Settings.root_dir self._mcu_files_model = None self._terminal = Terminal() self._terminal_dialog = None self._code_editor = None self._flash_dialog = None self.actionNavigate.triggered.connect(self.navigate_directory) self.actionTerminal.triggered.connect(self.open_terminal) self.actionCode_Editor.triggered.connect(self.open_code_editor) self.actionUpload.triggered.connect(self.upload_transfer_scripts) self.actionFlash.triggered.connect(self.open_flash_dialog) self.connectionComboBox.currentIndexChanged.connect( self.connection_changed) self.refreshButton.clicked.connect(self.refresh_ports) # Populate baud speed combo box and select default self.baudComboBox.clear() for speed in BaudOptions.speeds: self.baudComboBox.addItem(str(speed)) self.baudComboBox.setCurrentIndex(BaudOptions.speeds.index(115200)) self.presetButton.clicked.connect(self.show_presets) self.connectButton.clicked.connect(self.connect_pressed) self.update_file_tree() self.listButton.clicked.connect(self.list_mcu_files) self.listView.clicked.connect(self.mcu_file_selection_changed) self.listView.doubleClicked.connect(self.read_mcu_file) self.executeButton.clicked.connect(self.execute_mcu_code) self.removeButton.clicked.connect(self.remove_file) self.localPathEdit.setText(self._root_dir) self.treeView.clicked.connect(self.local_file_selection_changed) self.treeView.doubleClicked.connect(self.open_local_file) self.transferToMcuButton.clicked.connect(self.transfer_to_mcu) self.transferToPcButton.clicked.connect(self.transfer_to_pc) self.disconnected()
def find_knowns_of(terminal: Terminal, params: dict): profile_id = params.get('id', None) if not profile_id: terminal.error('No profile id provided') terminal.info('Try again using -profile-id=<profile id>') return else: terminal.info('Getting knowns of %s' % profile_id) try: knowns_of = facebook_api.get_knowns_of(profile_id) if len(knowns_of) > 0: known_directory = '%s/%s' % ( OUTPUT_CSVS_FRIENDS_DIR, profile_id) file_name = '%s/known_people.csv' % known_directory try: os.mkdir(known_directory) except FileExistsError: pass save_list_of_dicts(file_name, knowns_of, ['id', 'name']) except GraphAPIError as ex: terminal.error(str(ex))
def __init__( self, initial_seeds=3, number_of_games=10, animation_wait=0.001, player1=None, player2=None, ): self.term = Terminal() if number_of_games is None: number_of_games = GetUserInput( 'Enter number of games: ').get_response() self.number_of_games = number_of_games self.initial_seeds = initial_seeds self.animation_wait = animation_wait player_1_color = self.term.bold + self.term.red player_2_color = self.term.bold + self.term.blue if not player1: player_class = GetUserInput( 'Enter player type for Player 1:', PlayerFactory.all_classes()).get_response() self.player1 = player_class('Player 1', color=player_1_color, wait_time=animation_wait) else: self.player1 = player1 if not player2: player_class = GetUserInput( 'Enter player type for Player 2:', PlayerFactory.all_classes()).get_response() self.player2 = player_class('Player 2', color=player_2_color, wait_time=animation_wait) else: self.player2 = player2
class Game: def __init__(self) -> None: # basic init for pygame: window/screen, blackground, clock and fonts # fonts need to be initialized extra pygame.init() pygame.font.init() pygame.display.set_caption(GAME_NAME) self.window = pygame.display.set_mode( (WINDOW_SIZE_X + TERMINAL_WIDTH, WINDOW_SIZE_Y)) self.background = pygame.Surface( (TILE_HEIGHT * ROWS, TILE_WIDTH * COLS)) self.clock = pygame.time.Clock() self.terminal_font = pygame.font.SysFont(FONT_TYPE, TERMINAL_FONT_SIZE) self.description_font = pygame.font.SysFont(FONT_TYPE, DESCRIPTION_FONT_SIZE) self.game_over_font = pygame.font.SysFont(FONT_TYPE, GAME_OVER_FONT_SIZE) # init chessboard # an array of meeples is created that reflects the default setup # sprites are loaded from board list which are then added to the sprite group, because it is easier to draw them self.board = Chessboard() self.sprites = self.board.loadSprites() self.chess_sprite_list = pygame.sprite.Group() self.chess_sprite_list.add(self.sprites) # tile representation of the chessboard # used to determine tile/meeple on click events self.chessboard_tiles = self.board.loadTiles(self.background) # init terminal # clickable button without UX -> no highlighting are click reaction # terminal window that logs the moves performed by the players # notations for each player are saved to different arrays; if notations exceed 28 lines -> list.pop is used / no scrolling possible # instance variables for a white meeple that a player has selected and the possbile moves for the according meeple self.terminal = Terminal(self.terminal_font) # init black player self.bot = Bot() # draw terminal on the right side of the game window including a "new game" button drawTerminal(self) # conditions for the game loop self.gameRunning = True # condition for gameloop self.playerTurn = True # white starts playing self.selectedField = False # player has selected a field self.check_white = False self.check_black = False self.draw = False self.check_mate_white = False self.check_mate_black = False # init first possible moves for white player # saves the next possibles for each side self.number_of_moves = self.board.getAllMoves("w") def run(self): while self.gameRunning: if pygame.event.get(pygame.QUIT): sys.exit() # white player's turn if self.playerTurn: if pygame.event.get(pygame.MOUSEBUTTONDOWN): mouse_x, mouse_y = pygame.mouse.get_pos() # reset the game when clicking on the "new game" button by calling __init__ from Game if self.terminal.button.collidepoint((mouse_x, mouse_y)): self.__init__() self.board.getAllMoves("w") if self.number_of_moves > 0: self.check_white = False # finds selected tiles on the chessboard selected_tile = [ tile for tile in self.chessboard_tiles if tile.collidepoint((mouse_x, mouse_y)) ] # if a position outside the chessboard was selected, jump to the next iteration of the game loop # prevents out of bound exception if len(selected_tile) != 1: continue tile_x, tile_y = convert(selected_tile[0].x, selected_tile[0].y) # in this game status the white player has to select a meeple to see possible moves # a move can only be performed when a highlighted possible move is clicked if not self.selectedField: meeple = self.board.highlightMeeple( tile_x, tile_y) # choose from available moves # if the clicked meeple is a white meeple -> the according tiles are highlighted if meeple != None and meeple.colour == "w": moves = meeple.possible_moves self.board.highlightMoves(moves) self.selectedField = True else: # while possible moves (yellow tiles) are displayed, clicking on the highlighted meeple (green) or on any # other tile on the chessboard, desselects the possible moves and the highlighted meeple and returns to previous # if statement where the player has to select a meeple if not (tile_x, tile_y) in self.board.highlightedMoveTiles: self.board.highlightMoves([]) self.board.highlightedMeeple = None self.selectedField = False else: # if a hightlighted possible move is clicked (yellow tiles), the move to this tile is performed # sprites group is updated, check status calculated and move rendered on terminal self.board.highlightMoves([]) result = self.board.moveMeeple( (tile_x, tile_y)) self.updateSprites() self.check_black = self.board.king_b.isCheck( self.board) self.number_of_moves = self.board.getAllMoves( "b") self.displayNotation(result) self.playerTurn = False # black player's turn represented by a stupid computer player else: self.playerTurn = True self.selectedField = False # directly skip to end game screen when black is check mate; if possibles moves is > 0 check can be dissolved because # there are only moves calculated that "free" the king of check if self.number_of_moves > 0: # delay black side move a little bit to see that the black king was checked pygame.time.delay(400) self.check_black = False meeple = self.bot.run(self.board) meeple = self.board.highlightMeeple(meeple.x, meeple.y) result = self.board.moveMeeple( random.choice(meeple.possible_moves)) self.updateSprites() self.check_white = self.board.king_w.isCheck(self.board) self.number_of_moves = self.board.getAllMoves("w") self.displayNotation(result, "b") #draw GUI - end of loop self.drawChessboard() if self.draw or self.check_mate_black or self.check_mate_white: showGameOver(self) pygame.display.flip() self.clock.tick(30) # when a meeple is moved, the function is used to update the sprites group which is rendered thereafter def updateSprites(self): self.chess_sprite_list.empty() meeple_sprites = self.board.loadSprites() self.chess_sprite_list.add(meeple_sprites) def displayNotation(self, result: List, side: str = "w") -> None: if side == "w": if self.number_of_moves > 0 and self.check_black: result[6].append("check") self.check_black = True if self.number_of_moves < 1 and self.check_black: result[6].append("check_mate") self.check_mate_black = True if self.number_of_moves < 1 and not self.check_black: result[6].append("draw") self.draw = True else: if self.number_of_moves > 0 and self.check_white: result[6].append("check") self.check_white = True if self.number_of_moves < 1 and self.check_white: result[6].append("check_mate") self.check_mate_white = True if self.number_of_moves < 1 and not self.check_white: result[6].append("draw") self.draw = True self.terminal.addNotation(result, side) # important that all drawings on the chessboard are in one function so the layering can be controlled def drawChessboard(self) -> None: self.window.blit(self.background, (0, 0)) pygame.draw.rect(self.window, COLOUR_WHITE, self.terminal.button) pygame.draw.rect(self.window, COLOUR_WHITE, self.terminal.terminal_background) self.window.blit(self.terminal.button_text, (BUTTON_TEXT_X, BUTTON_TEXT_Y)) # indicator for the selected meeple and the resulting possible moves # selected player meeple is highlighted with a green rec while possible moves are indicated with yellow recs highlights = self.board.drawHighlightedMeeple( ) + self.board.drawHighlightedMoves() board_description = self.board.loadChessboardDescription( self.description_font) for highlight_info in highlights: if highlight_info != None: self.window.blit(highlight_info[0], (highlight_info[1], highlight_info[2])) # board descrption in red color that indicate the row and col for letter_info in board_description[0]: if letter_info != None: self.window.blit(letter_info[0], (letter_info[1], letter_info[2])) for number_info in board_description[1]: if number_info != None: self.window.blit(number_info[0], (number_info[1], number_info[2])) # draws text from terminal arrays to terminal box with a column for white (left side) including the numbered round and black (right side) for index, text in enumerate(self.terminal.terminal_white_notation): self.window.blit(text, (TERMINAL_TEXT_X_WHITE, TERMINAL_TEXT_Y + index * TERMINAL_TEXT_Y_OFFSET)) for index, text in enumerate(self.terminal.terminal_black_notation): self.window.blit(text, (TERMINAL_TEXT_X_BLACK, TERMINAL_TEXT_Y + index * TERMINAL_TEXT_Y_OFFSET)) # draws a red rectangle under the kings if they are check if self.check_black: surface = pygame.Surface((TILE_WIDTH, TILE_HEIGHT)) surface.set_alpha(COLOUR_ALPHA) surface.fill(COLOUR_RED) self.window.blit(surface, (self.board.king_b.x * TILE_HEIGHT, self.board.king_b.y * TILE_WIDTH)) if self.check_white: surface = pygame.Surface((TILE_WIDTH, TILE_HEIGHT)) surface.set_alpha(COLOUR_ALPHA) surface.fill(COLOUR_RED) self.window.blit(surface, (self.board.king_w.x * TILE_HEIGHT, self.board.king_w.y * TILE_WIDTH)) #draw sprites at last so they are on top of everything self.chess_sprite_list.draw(self.window)
class Game: def __init__( self, *, player1, player2, side_length=6, initial_seeds=None, player_1_color=None, player_2_color=None, animation_wait=0.1, ): self.term = Terminal() if player_1_color is None: player_1_color = self.term.bold + self.term.red if player_2_color is None: player_2_color = self.term.bold + self.term.blue seed_color = self.term.green index_color = self.term.yellow self.term.clear() self.term.move(Location(5, 5)) self.board = Board( side_length, seed_color=seed_color, index_color=index_color, animation_wait=animation_wait, ) self.player1 = player1 self.player2 = player2 self.board.assign_player(self.player1) self.board.assign_player(self.player2) self.current_player = self.player1 self._players = (self.player1, self.player2) seeds = initial_seeds or self._get_initial_seeds() self.board.initialize_cups(seeds) self.board.clear_board() self.board.display_cups() def _get_initial_seeds(self): seeds = input('Enter the initial number of seeds per cup: ') return seeds def clear_screen(self): self.term.clear() def run(self): self.clear_screen() while not self.board.done(): self.board.clear_board() self.board.display_cups() try: cup = self.current_player._take_turn() last_cup = self.board.sow(cup, color=self.current_player.color) except (EmptyCup, InvalidCup): continue self._determine_next_player(last_cup) self.clear_screen() self.board.display_cups() print() print() if self.board.player_1_cup > self.board.player_2_cup: print(f'{self.player1.name} Wins!') self.board.player1.game_over(Result.Win) self.board.player2.game_over(Result.Loss) elif self.board.player_1_cup < self.board.player_2_cup: print(f'{self.player2.name} Wins!') self.board.player1.game_over(Result.Loss) self.board.player2.game_over(Result.Win) else: print('Tie game!') self.board.player1.game_over(Result.Tie) self.board.player2.game_over(Result.Tie) def _determine_next_player(self, last_cup): if self.current_player == self.player1: if last_cup != self.board.player_1_cup_index: self.current_player = self.player2 elif self.current_player == self.player2: if last_cup != self.board.player_2_cup_index: self.current_player = self.player1
def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) self.setAttribute(Qt.WA_QuitOnClose) geometry = Settings().retrieve_geometry("main") if geometry: self.restoreGeometry(geometry) self._connection_scanner = ConnectionScanner() self._connection = None self._root_dir = Settings().root_dir self._mcu_files_model = None self._terminal = Terminal() self._terminal_dialog = None self._code_editor = None self._flash_dialog = None self._settings_dialog = None self._preset_password = None self.actionNavigate.triggered.connect(self.navigate_directory) self.actionTerminal.triggered.connect(self.open_terminal) self.actionCode_Editor.triggered.connect(self.open_code_editor) self.actionUpload.triggered.connect(self.upload_transfer_scripts) self.actionFlash.triggered.connect(self.open_flash_dialog) self.actionSettings.triggered.connect(self.open_settings_dialog) self.connectionComboBox.currentIndexChanged.connect(self.connection_changed) self.refreshButton.clicked.connect(self.refresh_ports) # Populate baud speed combo box and select default self.baudComboBox.clear() for speed in BaudOptions.speeds: self.baudComboBox.addItem(str(speed)) self.baudComboBox.setCurrentIndex(BaudOptions.speeds.index(115200)) self.presetButton.clicked.connect(self.show_presets) self.connectButton.clicked.connect(self.connect_pressed) self.update_file_tree() self.listButton.clicked.connect(self.list_mcu_files) self.mcuFilesListView.clicked.connect(self.mcu_file_selection_changed) self.mcuFilesListView.doubleClicked.connect(self.read_mcu_file) self.executeButton.clicked.connect(self.execute_mcu_code) self.removeButton.clicked.connect(self.remove_file) self.localPathEdit.setText(self._root_dir) local_selection_model = self.localFilesTreeView.selectionModel() local_selection_model.selectionChanged.connect(self.local_file_selection_changed) self.localFilesTreeView.doubleClicked.connect(self.open_local_file) # Set the "Name" column to always fit the content self.localFilesTreeView.header().setSectionResizeMode(0, QHeaderView.ResizeToContents) self.compileButton.clicked.connect(self.compile_files) self.update_compile_button() self.autoTransferCheckBox.setChecked(Settings().auto_transfer) self.transferToMcuButton.clicked.connect(self.transfer_to_mcu) self.transferToPcButton.clicked.connect(self.transfer_to_pc) self.disconnected()
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) self.setAttribute(Qt.WA_QuitOnClose) geometry = Settings().retrieve_geometry("main") if geometry: self.restoreGeometry(geometry) self._connection_scanner = ConnectionScanner() self._connection = None self._root_dir = Settings().root_dir self._mcu_files_model = None self._terminal = Terminal() self._terminal_dialog = None self._code_editor = None self._flash_dialog = None self._settings_dialog = None self._preset_password = None self.actionNavigate.triggered.connect(self.navigate_directory) self.actionTerminal.triggered.connect(self.open_terminal) self.actionCode_Editor.triggered.connect(self.open_code_editor) self.actionUpload.triggered.connect(self.upload_transfer_scripts) self.actionFlash.triggered.connect(self.open_flash_dialog) self.actionSettings.triggered.connect(self.open_settings_dialog) self.connectionComboBox.currentIndexChanged.connect(self.connection_changed) self.refreshButton.clicked.connect(self.refresh_ports) # Populate baud speed combo box and select default self.baudComboBox.clear() for speed in BaudOptions.speeds: self.baudComboBox.addItem(str(speed)) self.baudComboBox.setCurrentIndex(BaudOptions.speeds.index(115200)) self.presetButton.clicked.connect(self.show_presets) self.connectButton.clicked.connect(self.connect_pressed) self.update_file_tree() self.listButton.clicked.connect(self.list_mcu_files) self.mcuFilesListView.clicked.connect(self.mcu_file_selection_changed) self.mcuFilesListView.doubleClicked.connect(self.read_mcu_file) self.executeButton.clicked.connect(self.execute_mcu_code) self.removeButton.clicked.connect(self.remove_file) self.localPathEdit.setText(self._root_dir) local_selection_model = self.localFilesTreeView.selectionModel() local_selection_model.selectionChanged.connect(self.local_file_selection_changed) self.localFilesTreeView.doubleClicked.connect(self.open_local_file) # Set the "Name" column to always fit the content self.localFilesTreeView.header().setSectionResizeMode(0, QHeaderView.ResizeToContents) self.compileButton.clicked.connect(self.compile_files) self.update_compile_button() self.autoTransferCheckBox.setChecked(Settings().auto_transfer) self.transferToMcuButton.clicked.connect(self.transfer_to_mcu) self.transferToPcButton.clicked.connect(self.transfer_to_pc) self.disconnected() def closeEvent(self, event): Settings().root_dir = self._root_dir Settings().auto_transfer = self.autoTransferCheckBox.isChecked() Settings().update_geometry("main", self.saveGeometry()) Settings().save() if self._connection is not None and self._connection.is_connected(): self.end_connection() if self._terminal_dialog: self._terminal_dialog.close() if self._code_editor: self._code_editor.close() event.accept() def connection_changed(self): connection = self._connection_scanner.port_list[self.connectionComboBox.currentIndex()] self.connectionStackedWidget.setCurrentIndex(1 if connection == "wifi" else 0) def refresh_ports(self): self._connection_scanner.scan_connections(with_wifi=True) # Populate port combo box and select default self.connectionComboBox.clear() if self._connection_scanner.port_list: for port in self._connection_scanner.port_list: self.connectionComboBox.addItem(port) prefPort = str(Settings().preferred_port) prefPort = prefPort.upper() prefPort = prefPort.join(prefPort.split()) if self.connectionComboBox.findText(prefPort) >= 0: self.connectionComboBox.setCurrentIndex(self.connectionComboBox.findText(prefPort)) else: self.connectionComboBox.setCurrentIndex(0) self.connectButton.setEnabled(True) else: self.connectButton.setEnabled(False) def set_status(self, status): if status == "Connected": self.statusLabel.setStyleSheet("QLabel { background-color : none; color : green; font : bold;}") elif status == "Disconnected": self.statusLabel.setStyleSheet("QLabel { background-color : none; color : red; }") elif status == "Connecting...": self.statusLabel.setStyleSheet("QLabel { background-color : none; color : blue; }") elif status == "Error": self.statusLabel.setStyleSheet("QLabel { background-color : red; color : white; }") elif status == "Password": self.statusLabel.setStyleSheet("QLabel { background-color : red; color : white; }") status = "Wrong Password" else: self.statusLabel.setStyleSheet("QLabel { background-color : red; color : white; }") self.statusLabel.setText(status) QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) def update_compile_button(self): self.compileButton.setEnabled(bool(Settings().mpy_cross_path) and len(self.get_local_file_selection()) > 0) def disconnected(self): self.connectButton.setText("Connect") self.set_status("Disconnected") self.listButton.setEnabled(False) self.connectionComboBox.setEnabled(True) self.baudComboBox.setEnabled(True) self.refreshButton.setEnabled(True) self.mcuFilesListView.setEnabled(False) self.executeButton.setEnabled(False) self.removeButton.setEnabled(False) self.actionTerminal.setEnabled(False) self.actionUpload.setEnabled(False) self.transferToMcuButton.setEnabled(False) self.transferToPcButton.setEnabled(False) # Clear terminal on disconnect self._terminal.clear() if self._terminal_dialog: self._terminal_dialog.close() if self._code_editor: self._code_editor.disconnected() self.refresh_ports() def connected(self): self.connectButton.setText("Disconnect") self.set_status("Connected") self.listButton.setEnabled(True) self.connectionComboBox.setEnabled(False) self.baudComboBox.setEnabled(False) self.refreshButton.setEnabled(False) self.mcuFilesListView.setEnabled(True) self.actionTerminal.setEnabled(True) if isinstance(self._connection, SerialConnection): self.actionUpload.setEnabled(True) self.transferToMcuButton.setEnabled(True) if self._code_editor: self._code_editor.connected(self._connection) self.list_mcu_files() def navigate_directory(self): dialog = QFileDialog() dialog.setDirectory(self._root_dir) dialog.setFileMode(QFileDialog.Directory) dialog.setOption(QFileDialog.ShowDirsOnly) dialog.exec() path = dialog.selectedFiles() if path and path[0]: self._root_dir = path[0] self.localPathEdit.setText(self._root_dir) self.update_file_tree() def update_file_tree(self): model = QFileSystemModel() model.setRootPath(self._root_dir) self.localFilesTreeView.setModel(model) local_selection_model = self.localFilesTreeView.selectionModel() local_selection_model.selectionChanged.connect(self.local_file_selection_changed) self.localFilesTreeView.setRootIndex(model.index(self._root_dir)) def serial_mcu_connection_valid(self): file_list = [] try: file_list = self._connection.list_files() return True except OperationError: file_list = None return False def list_mcu_files(self): file_list = [] try: file_list = self._connection.list_files() except OperationError: QMessageBox().critical(self, "Operation failed", "Could not list files.", QMessageBox.Ok) return self._mcu_files_model = QStringListModel() for file in file_list: idx = self._mcu_files_model.rowCount() self._mcu_files_model.insertRow(idx) self._mcu_files_model.setData(self._mcu_files_model.index(idx), file) self.mcuFilesListView.setModel(self._mcu_files_model) self.mcu_file_selection_changed() def execute_mcu_code(self): idx = self.mcuFilesListView.currentIndex() assert isinstance(idx, QModelIndex) model = self.mcuFilesListView.model() assert isinstance(model, QStringListModel) file_name = model.data(idx, Qt.EditRole) self._connection.run_file(file_name) def remove_file(self): idx = self.mcuFilesListView.currentIndex() assert isinstance(idx, QModelIndex) model = self.mcuFilesListView.model() assert isinstance(model, QStringListModel) file_name = model.data(idx, Qt.EditRole) try: self._connection.remove_file(file_name) except OperationError: QMessageBox().critical(self, "Operation failed", "Could not remove the file.", QMessageBox.Ok) return self.list_mcu_files() def ask_for_password(self, title, label="Password"): if self._preset_password is not None: return self._preset_password input_dlg = QInputDialog(parent=self, flags=Qt.Dialog) input_dlg.setTextEchoMode(QLineEdit.Password) input_dlg.setWindowTitle(title) input_dlg.setLabelText(label) input_dlg.resize(500, 100) input_dlg.exec() return input_dlg.textValue() def start_connection(self): self.set_status("Connecting...") connection = self._connection_scanner.port_list[self.connectionComboBox.currentIndex()] if connection == "wifi": ip_address = self.ipLineEdit.text() port = self.portSpinBox.value() if not IpHelper.is_valid_ipv4(ip_address): QMessageBox().warning(self, "Invalid IP", "The IP address has invalid format", QMessageBox.Ok) return try: self._connection = WifiConnection(ip_address, port, self._terminal, self.ask_for_password) except PasswordException: self.set_status("Password") return except NewPasswordException: QMessageBox().information(self, "Password set", "WebREPL password was not previously configured, so it was set to " "\"passw\" (without quotes). " "You can change it in port_config.py (will require reboot to take effect). " "Caution: Passwords longer than 9 characters will be truncated.\n\n" "Continue by connecting again.", QMessageBox.Ok) return else: baud_rate = BaudOptions.speeds[self.baudComboBox.currentIndex()] self._connection = SerialConnection(connection, baud_rate, self._terminal, self.serialResetCheckBox.isChecked()) if self._connection.is_connected(): if not self.serial_mcu_connection_valid(): self._connection.disconnect() self._connection = None else: # serial connection didn't work, so likely the unplugged the serial device and COM value is stale self.refresh_ports() if self._connection is not None and self._connection.is_connected(): self.connected() if isinstance(self._connection, SerialConnection): if Settings().use_transfer_scripts and not self._connection.check_transfer_scripts_version(): QMessageBox.warning(self, "Transfer scripts problem", "Transfer scripts for UART are either" " missing or have wrong version.\nPlease use 'File->Init transfer files' to" " fix this issue.") else: self._connection = None self.set_status("Error") self.refresh_ports() def end_connection(self): self._connection.disconnect() self._connection = None self.disconnected() def show_presets(self): dialog = WiFiPresetDialog() dialog.accepted.connect(lambda: self.use_preset(dialog.selected_ip, dialog.selected_port, dialog.selected_password)) dialog.exec() def use_preset(self, ip, port, password): self.ipLineEdit.setText(ip) self.portSpinBox.setValue(port) self._preset_password = password def connect_pressed(self): if self._connection is not None and self._connection.is_connected(): self.end_connection() else: self.start_connection() def run_file(self): content = self.codeEdit.toPlainText() self._connection.send_block(content) def open_local_file(self, idx): assert isinstance(idx, QModelIndex) model = self.localFilesTreeView.model() assert isinstance(model, QFileSystemModel) if model.isDir(idx): return local_path = model.filePath(idx) remote_path = local_path.rsplit("/", 1)[1] if local_path.endswith(".py"): if Settings().external_editor_path: self.open_external_editor(local_path) else: with open(local_path) as f: text = "".join(f.readlines()) self.open_code_editor() self._code_editor.set_code(local_path, remote_path, text) else: QMessageBox.information(self, "Unknown file", "Files without .py ending won't open" " in editor, but can still be transferred.") def mcu_file_selection_changed(self): idx = self.mcuFilesListView.currentIndex() assert isinstance(idx, QModelIndex) if idx.row() >= 0: self.executeButton.setEnabled(True) self.removeButton.setEnabled(True) self.transferToPcButton.setEnabled(True) else: self.executeButton.setEnabled(False) self.removeButton.setEnabled(False) self.transferToPcButton.setEnabled(False) def get_local_file_selection(self): """Returns absolute paths for selected local files""" indices = self.localFilesTreeView.selectedIndexes() model = self.localFilesTreeView.model() assert isinstance(model, QFileSystemModel) def filter_indices(x): return x.column() == 0 and not model.isDir(x) # Filter out all but first column (file name) and # don't include directories indices = [x for x in indices if filter_indices(x)] # Return absolute paths return [model.filePath(idx) for idx in indices] def local_file_selection_changed(self): self.update_compile_button() local_file_paths = self.get_local_file_selection() if len(local_file_paths) == 1: self.remoteNameEdit.setText(local_file_paths[0].rsplit("/", 1)[1]) else: self.remoteNameEdit.setText("") def compile_files(self): local_file_paths = self.get_local_file_selection() compiled_file_paths = [] for local_path in local_file_paths: split = os.path.splitext(local_path) if split[1] == ".mpy": title = "COMPILE WARNING!! " + os.path.basename(local_path) QMessageBox.warning(self, title, "Can't compile .mpy files, already bytecode") continue mpy_path = split[0]+".mpy" try: os.remove(mpy_path) # Force view to update itself so that it sees file removed self.localFilesTreeView.repaint() QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) except OSError: pass try: with subprocess.Popen([Settings().mpy_cross_path, os.path.basename(local_path)], cwd=os.path.dirname(local_path), stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) as proc: proc.wait() # Wait for process to finish out = proc.stderr.read() if out: QMessageBox.warning(self, "Compilation error", out.decode("utf-8")) continue except OSError: QMessageBox.warning(self, "Compilation error", "Failed to run mpy-cross") continue compiled_file_paths += [mpy_path] # Force view to update so that it sees compiled files added self.localFilesTreeView.repaint() QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) # Force view to update last time so that it resorts the content # Without this, the new file will be placed at the bottom of the view no matter what header = self.localFilesTreeView.header() column = header.sortIndicatorSection() order = header.sortIndicatorOrder() # This is necessary so that view actually sorts anything again self.localFilesTreeView.sortByColumn(-1, Qt.AscendingOrder) QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) self.localFilesTreeView.sortByColumn(column, order) selection_model = self.localFilesTreeView.selectionModel() if compiled_file_paths: assert isinstance(selection_model, QItemSelectionModel) selection_model.clearSelection() for mpy_path in compiled_file_paths: idx = self.localFilesTreeView.model().index(mpy_path) selection_model.select(idx, QItemSelectionModel.Select | QItemSelectionModel.Rows) if (self.autoTransferCheckBox.isChecked() and self._connection and self._connection.is_connected() and compiled_file_paths): self.transfer_to_mcu() def finished_read_mcu_file(self, file_name, transfer): assert isinstance(transfer, FileTransfer) result = transfer.read_result text = result.binary_data.decode("utf-8", errors="replace") if result.binary_data is not None else "!Failed to read file!" self.open_code_editor() self._code_editor.set_code(None, file_name, text) def read_mcu_file(self, idx): assert isinstance(idx, QModelIndex) model = self.mcuFilesListView.model() assert isinstance(model, QStringListModel) file_name = model.data(idx, Qt.EditRole) if not file_name.endswith(".py"): QMessageBox.information(self, "Unknown file", "Files without .py ending won't open" " in editor, but can still be transferred.") return progress_dlg = FileTransferDialog(FileTransferDialog.DOWNLOAD) progress_dlg.finished.connect(lambda: self.finished_read_mcu_file(file_name, progress_dlg.transfer)) progress_dlg.show() self._connection.read_file(file_name, progress_dlg.transfer) def upload_transfer_scripts(self): progress_dlg = FileTransferDialog(FileTransferDialog.UPLOAD) progress_dlg.finished.connect(self.list_mcu_files) progress_dlg.show() self._connection.upload_transfer_files(progress_dlg.transfer) def transfer_to_mcu(self): local_file_paths = self.get_local_file_selection() progress_dlg = FileTransferDialog(FileTransferDialog.UPLOAD) progress_dlg.finished.connect(self.list_mcu_files) progress_dlg.show() # Handle single file transfer if len(local_file_paths) == 1: local_path = local_file_paths[0] remote_path = self.remoteNameEdit.text() with open(local_path, "rb") as f: content = f.read() self._connection.write_file(remote_path, content, progress_dlg.transfer) return # Batch file transfer progress_dlg.enable_cancel() progress_dlg.transfer.set_file_count(len(local_file_paths)) self._connection.write_files(local_file_paths, progress_dlg.transfer) def finished_transfer_to_pc(self, file_path, transfer): if not transfer.read_result.binary_data: return try: with open(file_path, "wb") as file: file.write(transfer.read_result.binary_data) except IOError: QMessageBox.critical(self, "Save operation failed", "Couldn't save the file. Check path and permissions.") def transfer_to_pc(self): idx = self.mcuFilesListView.currentIndex() assert isinstance(idx, QModelIndex) model = self.mcuFilesListView.model() assert isinstance(model, QStringListModel) remote_path = model.data(idx, Qt.EditRole) local_path = self.localPathEdit.text() + "/" + remote_path progress_dlg = FileTransferDialog(FileTransferDialog.DOWNLOAD) progress_dlg.finished.connect(lambda: self.finished_transfer_to_pc(local_path, progress_dlg.transfer)) progress_dlg.show() self._connection.read_file(remote_path, progress_dlg.transfer) def open_terminal(self): if self._terminal_dialog is not None: return self._terminal_dialog = TerminalDialog(self, self._connection, self._terminal) self._terminal_dialog.finished.connect(self.close_terminal) self._terminal_dialog.show() def close_terminal(self): self._terminal_dialog = None def open_external_editor(self, file_path): ext_path = Settings().external_editor_path ext_args = [] if Settings().external_editor_args: def wildcard_replace(s): s = s.replace("%f", file_path) return s ext_args = [wildcard_replace(x.strip()) for x in Settings().external_editor_args.split(";")] subprocess.Popen([ext_path] + ext_args) def open_code_editor(self): if self._code_editor is not None: return self._code_editor = CodeEditDialog(self, self._connection) self._code_editor.mcu_file_saved.connect(self.list_mcu_files) self._code_editor.finished.connect(self.close_code_editor) self._code_editor.show() def close_code_editor(self): self._code_editor = None def open_flash_dialog(self): if self._connection is not None and self._connection.is_connected(): self.end_connection() self._flash_dialog = FlashDialog(self) self._flash_dialog.finished.connect(self.close_flash_dialog) self._flash_dialog.show() def close_flash_dialog(self): self._flash_dialog = None def open_settings_dialog(self): if self._settings_dialog is not None: return self._settings_dialog = SettingsDialog(self) self._settings_dialog.finished.connect(self.close_settings_dialog) self._settings_dialog.show() def close_settings_dialog(self): self._settings_dialog = None # Update compile button as mpy-cross path might have been set self.update_compile_button()
from src.terminal import Terminal if sys.version_info[0] < 3: raise Exception("Python 3 or a more recent version is required.") user_request_exit = False ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) OUTPUT_DIR = join_path(ROOT_DIR, 'output') OUTPUT_REQUESTS_DIR = join_path(OUTPUT_DIR, 'requests') OUTPUT_CSVS_DIR = join_path(OUTPUT_DIR, 'csv') OUTPUT_CSVS_FRIENDS_DIR = join_path(OUTPUT_CSVS_DIR, 'known_people') OUTPUT_PHOTOS_DIR = join_path(ROOT_DIR, 'output/photos') OUTPUT_FRIENDS = join_path(OUTPUT_CSVS_DIR, 'known_people') COOKIES_DIR = join_path(ROOT_DIR, 'cookie') CONFIGURATION_DIR = join_path(ROOT_DIR, 'configuration') def define_directories(): # directories DIRS = [ OUTPUT_DIR, COOKIES_DIR, CONFIGURATION_DIR, OUTPUT_REQUESTS_DIR, OUTPUT_CSVS_DIR, OUTPUT_CSVS_FRIENDS_DIR, OUTPUT_PHOTOS_DIR ] for dir_path in DIRS: write_directory(dir_path) terminal = Terminal() terminal.logo('Open Source Social Information')
class Board: def __init__( self, side_length, animation_wait=1, seed_color=None, index_color=None, player1=None, player2=None, ): """ Indexes and their associated letters: a b c d e f g 1 2 3 4 5 6 7 0 8 15 14 13 12 11 10 9 h i j k l m n """ self.term = Terminal() self.side_length = side_length self.total_number_of_cups = self.side_length * 2 + 2 self.cups = [0] * self.total_number_of_cups self._SEED_COLOR = seed_color self._INDEX_COLOR = index_color self._ANIMATION_WAIT = animation_wait self._INITIAL_LOCATION = Location(5, 10) self._HORIZONTAL_SPACER = Location(0, 4) self._TOP_ROW_INDICATOR_LOCATION = (self._INITIAL_LOCATION + self._HORIZONTAL_SPACER) self._TOP_ROW_INDICES_LOCATION = self._TOP_ROW_INDICATOR_LOCATION + Location( 1, 0) self._TOP_ROW_LOCATION = self._TOP_ROW_INDICES_LOCATION + Location( 1, 0) self._PLAYER_1_LOCATION = Location(self._TOP_ROW_LOCATION.row + 1, self._INITIAL_LOCATION.column) self._PLAYER_2_LOCATION = self._PLAYER_1_LOCATION + Location( 0, self._HORIZONTAL_SPACER.column * (self.side_length + 1)) self._BOTTOM_ROW_LOCATION = (self._PLAYER_1_LOCATION + self._HORIZONTAL_SPACER + Location(1, 0)) self._BOTTOM_ROW_INDICES_LOCATION = self._BOTTOM_ROW_LOCATION + Location( 1, 0) self._BOTTOM_ROW_INDICATOR_LOCATION = ( self._BOTTOM_ROW_INDICES_LOCATION + Location(1, 0)) self._PLAYER_1_INDICATOR = self._PLAYER_1_LOCATION - self._HORIZONTAL_SPACER self._PLAYER_2_INDICATOR = self._PLAYER_2_LOCATION + self._HORIZONTAL_SPACER self._build_index_dicts() self.players = [] if player1: self.assign_player(player1) if player2: self.assign_player(player2) @property def ready_to_play(self): return len(self.players) == 2 def assign_player(self, player): if len(self.players) == 2: raise TooManyPlayers('Cannot add any more players to this board') if not self.players: self.player1 = player self.player1.assign_board(self) elif len(self.players) == 1: self.player2 = player self.player2.assign_board(self) self.players.append(player) @property def max_row(self): return self._BOTTOM_ROW_INDICATOR_LOCATION.row @property def max_column(self): return self._PLAYER_2_INDICATOR.column @property def min_row(self): return self._INITIAL_LOCATION.row @property def min_column(self): return self._INITIAL_LOCATION.column @property def top_row(self): return self.cups[1:self._midpoint] @property def top_row_indices(self): return [i for i in range(1, self._midpoint)] @property def top_row_cups(self): return sorted([self.index_to_cup[idx] for idx in self.top_row_indices]) @property def bottom_row(self): return self.cups[self._midpoint + 1:] @property def bottom_row_indices(self): return [i for i in range(self._midpoint + 1, len(self.cups))] @property def bottom_row_cups(self): return sorted( [self.index_to_cup[idx] for idx in self.bottom_row_indices]) @property def player_1_cup_index(self): return 0 @property def player_2_cup_index(self): return self._midpoint @property def player_1_cup(self): return self.cups[self.player_1_cup_index] @property def player_2_cup(self): return self.cups[self.player_2_cup_index] @property def _midpoint(self): return len(self.cups) // 2 def cup_seeds(self, cup): return self.cups[self.cup_to_index[cup]] def cup_seeds_by_index(self, index): return self.cups[index] @property def cup_to_index(self): return self._cup_to_index @property def index_to_cup(self): return self._index_to_cup def _build_index_dicts(self): self._cup_to_index = dict() self._index_to_cup_indicator = dict() letter_sequence = generate_sequence(len(self.cups) - 2) for i in range(1, self._midpoint): letter = letter_sequence.pop(0) self._cup_to_index[letter] = i for i in range(len(self.cups) - 1, self._midpoint, -1): letter = letter_sequence.pop(0) self._cup_to_index[letter] = i self._index_to_cup = {v: k for k, v in self._cup_to_index.items()} for i in range(len(self.cups)): if i == 0: # Player1's cup self._index_to_cup_indicator[i] = Indicator( self._PLAYER_1_INDICATOR, '>') elif i < self._midpoint: # Top row self._index_to_cup_indicator[i] = Indicator( (i - 1) * self._HORIZONTAL_SPACER + self._TOP_ROW_INDICATOR_LOCATION, 'v', ) elif i == self._midpoint: # Player2's cup self._index_to_cup_indicator[i] = Indicator( self._PLAYER_2_INDICATOR, '<') else: # Bottom row cups self._index_to_cup_indicator[i] = Indicator( (len(self.cups) - i - 1) * self._HORIZONTAL_SPACER + self._BOTTOM_ROW_INDICATOR_LOCATION, '^', ) def clear_indicators(self): for idx, indicator in self._index_to_cup_indicator.items(): self.term.move(indicator.location) self.term.display(' ') def done(self): return all(val == 0 for val in itertools.chain(self.top_row, self.bottom_row)) def initialize_cups(self, seeds): for i in range(len(self.cups)): if i == 0 or i == self._midpoint: continue self.cups[i] = int(seeds) def sow(self, cup, color=None): if cup not in self.cup_to_index: raise InvalidCup(f'Invalid cup. Got {cup}.') self.clear_indicators() index = self.cup_to_index[cup] seeds = self.cups[index] if seeds == 0: raise EmptyCup(f"Cup '{cup}' is empty.") self._draw_indicator(index, color=color) print() self.cups[index] = 0 self.display_cups() time.sleep(self._ANIMATION_WAIT) while seeds: index += 1 self.cups[index % len(self.cups)] += 1 seeds -= 1 self.clear_board() self.display_cups() self._draw_indicator(index % len(self.cups), color=color) print() time.sleep(self._ANIMATION_WAIT) return index % len(self.cups) def _draw_indicator(self, index, color=None): indicator = self._index_to_cup_indicator[index] self.term.move(indicator.location) self.term.display(indicator.symbol, color=color) def clear_board(self): for row in range(self.min_row, self.max_row): # Iterate to max_columns + 1 in case player2's cup has 10 or more seeds for column in range(self.min_column, self.max_column + 1): self.term.move(row, column) self.term.display(' ') self.clear_indicators() def display_cups(self): self.term.move(self._PLAYER_1_LOCATION) self.term.display(self.player_1_cup, color=self.player1.color) self.term.move(self._PLAYER_2_LOCATION) self.term.display(self.player_2_cup, color=self.player2.color) # Draw the top row # Draw cup indices current_location = self._TOP_ROW_INDICES_LOCATION for key in self.top_row_cups: self.term.move(current_location) self.term.display(key, color=self._INDEX_COLOR) current_location += self._HORIZONTAL_SPACER current_location = self._TOP_ROW_LOCATION for val in self.top_row: self.term.move(current_location) self.term.display(val, color=self._SEED_COLOR) current_location += self._HORIZONTAL_SPACER # Draw player cups # Draw bottom row current_location = self._BOTTOM_ROW_LOCATION for val in reversed(self.bottom_row): self.term.move(current_location) self.term.display(val, color=self._SEED_COLOR) current_location += self._HORIZONTAL_SPACER current_location = self._BOTTOM_ROW_INDICES_LOCATION for key in self.bottom_row_cups: self.term.move(current_location) self.term.display(key, color=self._INDEX_COLOR) current_location += self._HORIZONTAL_SPACER
def __init__( self, side_length, animation_wait=1, seed_color=None, index_color=None, player1=None, player2=None, ): """ Indexes and their associated letters: a b c d e f g 1 2 3 4 5 6 7 0 8 15 14 13 12 11 10 9 h i j k l m n """ self.term = Terminal() self.side_length = side_length self.total_number_of_cups = self.side_length * 2 + 2 self.cups = [0] * self.total_number_of_cups self._SEED_COLOR = seed_color self._INDEX_COLOR = index_color self._ANIMATION_WAIT = animation_wait self._INITIAL_LOCATION = Location(5, 10) self._HORIZONTAL_SPACER = Location(0, 4) self._TOP_ROW_INDICATOR_LOCATION = (self._INITIAL_LOCATION + self._HORIZONTAL_SPACER) self._TOP_ROW_INDICES_LOCATION = self._TOP_ROW_INDICATOR_LOCATION + Location( 1, 0) self._TOP_ROW_LOCATION = self._TOP_ROW_INDICES_LOCATION + Location( 1, 0) self._PLAYER_1_LOCATION = Location(self._TOP_ROW_LOCATION.row + 1, self._INITIAL_LOCATION.column) self._PLAYER_2_LOCATION = self._PLAYER_1_LOCATION + Location( 0, self._HORIZONTAL_SPACER.column * (self.side_length + 1)) self._BOTTOM_ROW_LOCATION = (self._PLAYER_1_LOCATION + self._HORIZONTAL_SPACER + Location(1, 0)) self._BOTTOM_ROW_INDICES_LOCATION = self._BOTTOM_ROW_LOCATION + Location( 1, 0) self._BOTTOM_ROW_INDICATOR_LOCATION = ( self._BOTTOM_ROW_INDICES_LOCATION + Location(1, 0)) self._PLAYER_1_INDICATOR = self._PLAYER_1_LOCATION - self._HORIZONTAL_SPACER self._PLAYER_2_INDICATOR = self._PLAYER_2_LOCATION + self._HORIZONTAL_SPACER self._build_index_dicts() self.players = [] if player1: self.assign_player(player1) if player2: self.assign_player(player2)
def find_knowns_of_my_knowns(terminal: Terminal): terminal.info('Getting knowns of all') knowns = facebook_api.get_knowns() for known in knowns: find_knowns_of(terminal, {'id': known['id']})