Chess Tournament Simulator
Use immutable sequences
Turn
NAMES = ['Jimmy', 'Samie', 'Elani', 'Aron', 'Emir', 'Sven', 'Nelson',
         'Antonio', 'Isabel', 'Wally', 'Li', 'Noam', 'Francis', 'Danya',
         'Danny', 'Engine']
         
RATINGS = [600, 900, 400, 700, 1000, 1100,
           1300, 1500, 1600, 1800, 2000,
           2200, 2300, 2650, 2500, 1000]

into tuples instead of lists, because they're constants.
Use data classes
class Player:
    &quot;&quot;&quot;This class holds the player information&quot;&quot;&quot;

    def __init__(self, name, rating):
        self.name = name
        self.rating = rating
        self.opponent = None

    def __repr__(self):
        return 'Player({}, {})'.format(self.name, self.rating)

does not need an explicit __init__ if you make it a @dataclass:
@dataclass
class Player:
    &quot;&quot;&quot;This class holds the player information&quot;&quot;&quot;
    name: str
    rating: int
    opponent: Optional['Player']

though I don't think that it's appropriate to represent opponent as an attribute of a player. That belongs to the game state.
Sequence representation
Consider changing
    self.tournament_rounds = {1: 'Round 1', 2: 'Round 2', 3: 'Semi-final', 4: 'Final'}

into a global (or class-static) tuple of strings:
TOURNAMENT_ROUNDS = ('Round 1', 'Round 2', 'Semi-final', 'Final')

The indices as keys are redundant.
Iterators and responsibilities
def next_round(self):
    &quot;&quot;&quot;Go the next round of the tournament&quot;&quot;&quot;

    self.game_round += 1

    try:
        print(self.tournament_rounds[self.game_round])
    except KeyError:
        return

and
            print(f'{player.name} wins this round by checkmate')
            player.rating += self.game_round * 200

seem dicey to me in terms of OOP representation. You'd be better-off having the game class yield a generator of rounds. This will make the game instance reusable.
Your score_game has the following responsibilities baked in - game simulation, and game presentation. These need to be separated - your score_game should not be printing. Also, score_game is itself not a great name, since it's not just scoring the game - it's actually running the game simulation.
Random selection
        maximum_range = 1000
        player_range = [i for i in range(0, int(player_chance * maximum_range))]
        choice = random.randint(0, maximum_range)
        if choice in player_range:

needs to be re-thought. Currently you're constructing a list, in memory, of every single number from 0 through (up to) 1000 only to do a membership test and then throw the whole thing away. Instead, just generate a random number and compare it to an upper bound.
Sleepy code
Don't arbitrarily add sleep. If you want to space out your console interface between logical operations, wait for a keypress.
Suggested
&quot;&quot;&quot;Chess Game simulator&quot;&quot;&quot;

from dataclasses import dataclass
from random import shuffle, randrange
from typing import ClassVar, Tuple, Iterable, Dict


@dataclass
class Player:
    name: str
    rating: int

    def __str__(self):
        return f'{self.name}({self.rating})'


CHESS_PLAYERS = [
    Player(name, rating)
    for rating, name in (
        ( 600, 'Jimmy'),
        ( 900, 'Samie'),
        ( 400, 'Elani'),
        ( 700, 'Aron'),
        (1000, 'Emir'),
        (1100, 'Sven'),
        (1300, 'Nelson'),
        (1500, 'Antonio'),
        (1600, 'Isabel'),
        (1800, 'Wally'),
        (2000, 'Li'),
        (2200, 'Noam'),
        (2300, 'Francis'),
        (2650, 'Danya'),
        (2500, 'Danny'),
        (1000, 'Engine'),
    )
]


class Match:
    def __init__(self, p1: Player, p2: Player):
        self.p1, self.p2 = p1, p2
        total = self.p1.rating + self.p2.rating
        if randrange(total) &lt; self.p1.rating:
            self.winner = self.p1
        else:
            self.winner = self.p2

    def __str__(self):
        return f'{self.p1} vs {self.p2}'

    @property
    def outcome(self) -&gt; str:
        return f'{self.winner} wins this round by checkmate'


class Round:
    def __init__(self, name: str, matches: Iterable[Match]):
        self.name = name
        self.matches = tuple(matches)

    def print(self):
        print(self.name)
        print('-'*45)
        print('\n'.join(str(match) for match in self.matches))
        print('-'*45)
        print('\n'.join(match.outcome for match in self.matches))


class ChessGame:
    ROUND_NAMES: ClassVar[Tuple[str]] = ('Round 1', 'Round 2', 'Semi-final', 'Final')

    def __init__(self, players: Iterable[Player]):
        self.players: Tuple[Player] = tuple(players)

    def __iter__(self) -&gt; Iterable[Round]:
        players = list(self.players)

        for i_round, name in enumerate(self.ROUND_NAMES):
            shuffle(players)
            half = len(players)//2
            round = Round(
                name,
                (
                    Match(p1, p2)
                    for p1, p2 in zip(players[:half], players[half:])
                )
            )
            yield round

            for match in round.matches:
                match.winner.rating += (i_round + 1) * 200

            players = [match.winner for match in round.matches]

    def print_run(self):
        for round in self:
            round.print()
            print()


def main():
    sim = ChessGame(CHESS_PLAYERS)
    sim.print_run()


if __name__ == '__main__':
    main()

The biggest 'gotcha' here is that, whereas the ChessGame is now reusable, player instances (just as in your original code) are mutated, so if you want to do repeated simulations you have to make copies of your player instances.