This program has four CPU levels. On this page, we evaluate the playing strength of each level.

Level 4 uses a complete evaluation database (stored in MongoDB) and therefore plays perfectly. However, because of its size, it is available only in the local CLI/GUI versions; the online version supports levels 1–3 only. Levels 1–3 use a subset of this database called the evalmap; both reliance on the evalmap and search depth vary by level.

Round-robin league

To assess strength, we ran a round-robin league among Dodgem CPU levels L1–L4. Results are shown below.

  • Rows are the first player (First); columns are the second player (Second).
  • Each cell is Wins–Losses–Draws from the first player’s perspective over 100 games.

3x3 board

First \ Second L1 L2 L3 L4
L1 59-41-0 17-83-0 40-60-0 39-61-0
L2 87-13-0 78-19-3 85-15-0 78-22-0
L3 100-0-0 100-0-0 100-0-0 100-0-0
L4 100-0-0 100-0-0 100-0-0 100-0-0
  • Both L3 and L4 won every match when playing first, demonstrating perfect play.

4x4 board

First \ Second L1 L2 L3 L4
L1 77-23-0 49-47-4 2-48-50 0-29-71
L2 91-7-2 63-28-9 3-34-63 0-14-86
L3 100-0-0 100-0-0 0-0-100 0-0-100
L4 92-0-8 86-0-14 0-0-100 0-0-100
  • L4 did not lose a single match, demonstrating perfect play.
  • L3 did not lose any matches when playing first, but lost a few when playing second, indicating that while its play is not perfect, it remains very difficult to beat.

5x5 board

First \ Second L1 L2 L3 L4
L1 57-43-0 42-57-1 28-61-11 0-59-41
L2 69-29-2 67-31-2 38-50-12 0-40-60
L3 81-13-6 80-15-5 56-26-18 0-34-66
L4 78-0-22 69-0-31 65-0-35 0-0-100
  • L4 did not lose a single match, demonstrating perfect play.
  • L3 is far from perfect play, because the complete evaluation database for 5×5 is enormous (164 million positions), while L3 uses only a small fraction (0.5%) of it.

Code

The following code was used for the round-robin matches on this page.

def main():
    import argparse
    description = "Round-robin league of dodgem"

    parser = argparse.ArgumentParser(
        description=description
    )
    parser.add_argument('-n', '--num', type=int,
                        default=4, help='board size (3-5)')
    parser.add_argument('-r', '--rep', type=int, default=100,
                        help='repetition (default: 100)')
    args = parser.parse_args()
    league(args.num, args.rep)


def league(num, rep):
    from dodgem import Dodgem
    print(f'A round-robin league on a {num}x{num} board, with {rep} games per paring.', flush=True)
    d = Dodgem(num)
    d.verbose = 0
    for sente in range(1, 5):
        for gote in range(1, 5):
            if sente != gote:
                d.refresh_evalmap = True
            else:
                d.refresh_evalmap = False
            d.level = [sente, gote]
            win, loss, draw = d.play_games(rep)
            print(f'L{sente}-L{gote}: {win}-{loss}-{draw}', flush=True)


if __name__ == "__main__":
    main()