This project is an open-source implementation of the symbolic search approaches described in
Edelkamp, S., & Kissmann, P. (2011, August). On the complexity of BDDs for state space search: A case study in Connect Four.
In Proceedings of the AAAI Conference on Artificial Intelligence (Vol. 25, No. 1, pp. 18-23).
and
Edelkamp, S., & Kissmann, P. (2008). Symbolic classification of general two-player games.
In KI 2008: Advances in Artificial Intelligence: 31st Annual German Conference on AI, KI 2008, Kaiserslautern, Germany, September 23-26, 2008. Proceedings 31 (pp. 185-192). Springer Berlin Heidelberg.
to count the number of unique positions and to produce a strong solution of a Connect4 game of a given board size, respectively.
The number of positions for the conventional 7 x 6 board can be computed in ~3h with 16 GB RAM.
The strong solution of the 7 x 6 instance takes ~47 hours and 128 GB RAM. It is published on Zenodo.
You can find a paper describing this work here.
In src/bdd, you can find a simple library to work with binary decision diagrams.
It is designed to give the user great control over memory.
The user specifies the number of all allocatable BDD nodes with the log2_tablesize parameter.
For instance, log2_tablesize=28 fits into 16 GB RAM and allows you to allocate 2^28 = 268,435,456 nodes simultaniously.
If you increase, log2_tablesize by one, it doubles the required RAM and number of allocatable nodes.
The maximum number is log2_tablesize=32.
The user has to manually keep track of BDD node references and manually perform garbage collection to free and reuse nodes, see src/bdd/uniquetable.h.
To demonstrate the usage of the library, we also include an implementation of the n-queens puzzle.
Go to its directory, cd src/queens, and compile it with make.
Run with queens.out log2_tablesize N, where N is the board size.
E.g. queens.out 22 8 or queens.out 27 12.
You may play around with trading of speed versus memory usage. E.g. queens.out 24 12.
Go to src/connect4.
There are multiple ways to encode a Connect4 position with Boolean variables.
Edelkamp and Kissmann use 2 * width * height + 1 variables: one variable denotes the side-to-move and there are two variables per board cell indicating whether it is empty, occupied by the first player, or occupied by the second player.
For this cell-wise encoding, Edelkamp and Kissmann proved that the algorithmic complexity of counting all unique positions is exponential in min(width, height), independent of variable ordering.
We have implemented their encoding for both a row-wise and column-wise order.
To encode transititoins, we need two sets of variable, i.e. two boards.
While they have not explicitly mentioned how to order the variables belonging to the same cell we take following approach: player-1-board-1, player-1-board-2, player-2-board-1, player-2-board-2, as illustrated below on the left.

Inspired by bitboard representations~\cite{tromp2008solving,Herzberg}, we have also implemented a more compressed encoding with only width * (height + 1)variables, illustrated above on the right.
In this encoding, each cell has only one variable.
The lowest available cell per column is always true.
All cells below the lowest available cell are occupied and true if the disc belongs to the first player else false (the disc belongs to the second player).
To facilitate this logic, we need an additional row.
This encoding outperforms the standard encoding for the 7 x 6 board configuration, but performs poorly if the number of rows is large.
Compile with make count.
Options:
COMPRESSED_ENCODING: whether to use compressed encoding (default:1)ALLOW_ROW_ORDER: whether to sort variables by row instead of column ifheigth > width. Is irrelevant ifheight <= width(default:0)FULLBDD: whether to compute BDD that represents all unique positions (= union of positions per ply, default:0)SUBTRACT_TERM: whether to use Connect4 termination criterion (default:1)ENTER_TO_CONTINUE: whether to require to press enter to start computation (default:0)WRITE_TO_FILE: whether to write results to.csvfile (default:1)IN_OP_GC: whether to allow automatic garbage collection during BDD operations (default:1)IN_OP_GC_THRES: fill-level of node pool above which garbage collection is triggerd ifIN_OP_GC=1(default:0.9999)IN_OP_GC_EXCL: whether to exclusively perform automatic garbage collection instead of at manually set points (default:0)
Run with ./build/counts.out log2_tablesize width height.
Generally, if you have enough memory available, log2_tablesize should be chosen such that the maximum fill-level of the node pool does not exceed 50 %.
Otherwise, performance degrades.
If the garbage collection manually set program points is not enough, then you may encounter a uniquetable too small error, i.e. there are no more nodes available to allocate.
In this case you may try to set IN_OP_GC=1.
Then, if the fill level exceeds IN_OP_GC_THRES during a BDD operation, garbage collection will be triggered.
This wrecks performance, because all caches are also cleared, which are heavily used during operations.
For IN_OP_GC=1 (and gariage collection in general) to work, the program has to be written in a way such that all BDD nodes that are used later on will be kept alive with reference counting, i.e. keep_alive(node).
Lastly, setting FULLBDD=1 increases computation and memory usage slighlty, because a union over all the BDD encoding the position at each ply is iteratively performed and kept alive.
Set only if you are intersted in the number of nodes required to encode all positions, not just the position counts.
Some examples to demonstrate flags:
-
make count COMPRESSED_ENCODING=1 FULLBDD=0 IN_OP_GC=0./build/count.out 29 7 6: uses <32 GB RAM, fill-level: 28.50 %, finishes in ~6300 seconds../build/count.out 28 7 6: uses <16 GB RAM, fill-level: 56.99 %, finsihes in ~6900 seconds.
-
make count COMPRESSED_ENCODING=1 FULLBDD=0 IN_OP_GC=1./build/count.out 27 7 6: uses <8 GB RAM, triggers IN_OP_GC 3 times, finishes in ~10800 seconds.
-
make count COMPRESSED_ENCODING=0 FULLBDD=0 IN_OP_GC=0./build/count.out 29 7 6uses <32 GB RAM, fill-level: 44.43 %, finsihes in ~10100 seconds../build/count.out 28 7 6uses <16 GB RAM, fill-level: 88.86 %, finishes in ~11700 seconds.
-
make count COMPRESSED_ENCODING=1 FULLBDD=1 IN_OP_GC=0./build/count.out 29 7 6uses <32 GB RAM, fill-level: 48.21 %, finishes in ~8000 seconds.
-
make count COMPRESSED_ENCODING=0 FULLBDD=1 IN_OP_GC=0./build/count.out 29 7 6uses <32 GB RAM, fill-level: 75.40 %, finishes in ~12800 seconds.
-
make count COMPRESSED_ENCODING=1 FULLBDD=1 IN_OP_GC=0 SUBTRACT_TERM=0./build/count.out 29 7 6uses <32 GB RAM, fill-level: 0.17 %, finishes in ~70 seconds.
If this takes too long on your computer you may try:
make count COMPRESSED_ENCODING=1 FULLBDD=0 IN_OP_GC=0./build/count.out 26 6 6: uses <4 GB RAM, fill-level: 60.02 %, finishes in in ~980 seconds.
Compile with make solve.
Options:
COMPRESSED_ENCODINGALLOW_ROW_ORDERENTER_TO_CONTINUEWRITE_TO_FILEIN_OP_GCIN_OP_GC_THRESIN_OP_GC_EXCLSAVE_BDD_TO_DISK: whether to save the strong solution as BDD to disk (default:1)
Run with ./build/solve.out log2_tablesize width height.
If SAVE_BDD_TO_DISK=1, then for each ply three files will be generated. One BDD that is sat for won positions, one for drawn, and one for lost positions.
These files will be stored in the solution_w{WIDTH}_h{HEIGHT} folder.
For information on other options, see previous section.
Some example runs:
make solve COMPRESSED_ENCODING=1./build/solve.out 29 6 6: uses <32 GB RAM, fill-level: 51.79 %, finishes in ~7700 seconds../build/solve.out 24 6 4: uses <1 GB RAM, fill-level: 12.87 %, finishes in ~35 seconds../build/solve.out 24 5 5: uses <1 GB RAM, fill-level: 14.22 %, finishes in ~40 seconds.
make solve COMPRESSED_ENCODING=0 IN_OP_GC=1 IN_OP_GC_THRES=0.9./build/solve.out 29 6 6: uses <32 GB RAM, fill-level: 90.00 %, finishes in ~15400 seconds.
make solve COMPRESSED_ENCODING=1 IN_OP_GC=1 IN_OP_GC_THRES=0.9./build/solve.out 28 6 6: uses <16 GB RAM, fill-level: 90.00 %, finishes in ~8500 seconds../build/solve.out 31 7 6: uses <128 GB RAM, fill-level: 90.00 %, finishes in ~168000 seconds. (See results section for output and Zenodo)
To query the solution generated with solve.out build the wdl.out program with make wdl.
Options:
COMPRESSED_ENCODINGALLOW_ROW_ORDERWIDTH: the width of the Connect4 boardHEIGHT: the height of the Connect4 board
The options have to be set to be compatable with the solutoin generated with solve.out.
Same encoding + same height and width.
Example:
make wdl COMPRESSED_ENCODING=1 WIDTH=6 HEIGHT=6
Output of ./build/wdl_w7_h6.out solution_w7_h6/ 332
input move sequence: 332
Connect4 width=7 x height=6
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . . stones played: 3
. . . o . . . side to move: o
. . x x . . . is terminal: 0
0 1 2 3 4 5 6
Overall evaluation = 1 (forced win)
move evaluation:
0 1 2 3 4 5 6
-1 1 -1 -1 1 -1 -1
1 ... move leads to forced win,
0 ... move leads to forced draw,
-1 ... move leads to forced loss
To find the move that leads to the fastest win or slowest loss compile with make bestmove
Options:
COMPRESSED_ENCODINGALLOW_ROW_ORDERWIDTH: the width of the Connect4 boardHEIGHT: the height of the Connect4 board
The options have to be set to be compatable with the solutoin generated with solve.out.
Same encoding + same height and width.
make wdl COMPRESSED_ENCODING=1 WIDTH=6 HEIGHT=6
Output of ./build/bestmove_w7_h6.out solution_w7_h6/ 332
input move sequence: 332
Connect4 width=7 x height=6
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . . stones played: 3
. . . o . . . side to move: o
. . x x . . . is terminal: 0
0 1 2 3 4 5 6
Overall evaluation = 1 (forced win)
Computing distance to mate ...
Position is 1 (win in 37 plies)
Best move is 4
n_nodes = 600314 in 0.004s (171506.525 knps)
move evaluation:
x ... forced win in x plies,
0 ... move leads to forced draw,
-x ... forced loss in x plies
±x ... search in progress (lower bound on final x)
0 1 2 3 4 5 6
-4 37 -4 -4 37 -4 -4
n_nodes = 5524605 in 0.022s (255009.459 knps)
best move: 1 with win in 37 plies
Connect4 width=7 x height=6
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . . stones played: 4
. . . o . . . side to move: x
. o x x . . . is terminal: 0
0 1 2 3 4 5 6
The bestmove search can be sped up by creating an openingbook by evaluating all positions at a given ply (default = 8).
Compile with make openingbook.
Options:
COMPRESSED_ENCODINGALLOW_ROW_ORDERWIDTH: the width of the Connect4 boardHEIGHT: the height of the Connect4 board
The options have to be set to be compatable with the solutoin generated with solve.out.
Same encoding + same height and width.
Run with generate_openingbook_w{WIDTH}_h{HEIGHT}.out folder n_workers.
For example generate_openingbook_w6_h6.out solution_w6_h6/ 4 creates the openingbook with 4 threads.
It may be beneficial to do multi-processing as well.
For this execute python3 probe/generate_openingbook_mp.py folder width height n_workers.
It will spawn n_workers / 4 processes.
The openingbook is stored as .csv where the position key (see position_key function in src/connect4/probe/board.c) is mapped to game score: 100 - ply for win in ply moves and -100 + ply for loss in ply moves.
If a position is not contained in the opening book it is a draw.
The bestmove search does alpha-beta pruning by leveraging the strong-solution found with solve.out.
For comparison, we also implemented an equivalent alpha-beta search that does not make use of the strong solution.
Compile with make full_ab_search.
Options:
WIDTH: the width of the Connect4 boardHEIGHT: the height of the Connect4 boardDTM: whether search move with respect to distance to mate (fastest win, slowest loss). Otherwise, only produces a win-draw-loss evaluation.
Run with ./build/full_ab_search_w{WIDTH}_h{HEIGHT}.out moveseq.
Examples:
make full_ab_search WIDTH=7 HEIGHT=6 DTM=1- finds solution in ~130 seconds and 946,511,712 nodes
make full_ab_search WIDTH=7 HEIGHT=6 DTM=0- finds solution in ~115 seconds and 827,622,460 nodes
There is a paper in progress and we have added scripts to conveniently reproduce the results.
We ran all experiments on a AMD Ryzen 9 5950X 16-Core Processor with 128 GB RAM.
python3 scripts/count_positions.py RAM max_workercounts the number of unique positions for several width-height combinations and several encodings. Results are stored inresults/count_positions_results. The optionRAMis the amount of memory you want to use, should be a multiple of 2. Andmax_workeris the maximum number of parallel searches. We ran the experiment withRAM=128andmax_worker=32. This takes days to complete.python3 scripts/analyse_count_results.pycan be used to create summary tables of the count results.python3 python3 scripts/count_w_and_wo_subtract_term.py 7 6 29was used to compare the BDD sizes when counting the number of unique positions for the 7 x 6 board with different encodings, once with the termination criteria and once without.python3 scripts/solve_position.py WIDTH HEIGHT LOG_TB_SIZE COMPRESSED_ENCODING ALLOW_ROW_ORDERcan be used to compile and run thesolve.outprogram with the specified parameters. We ranpython3 scripts/solve_position.py 7 6 31 1 0to solve the 7 x 6 board with 128 GB RAM in 47 hours.python3 scripts/generate_opening_book.py WIDTH HEIGHT COMPRESSED_ENCODING ALLOW_ROW_ORDER N_WORKERScan be used to conveniently generate an opening book for the strong solution obtained with the previous script. For the 7 x 6 case it took a little more than 5 hours with 32 workers.python3 scripts/print_solve_table.pycan be used to inspect the statistics per ply of the strong solution.python3 scripts/compress_solution.pywas used to compress the strong solution with7zipfor upload at Zenodo. Here we did not include the redundantdrawBDDs to decrease the size.python3 scripts/zip_results.py results/was used to zip the logs and data results from the experiments for upload at Zenodo.
We were able to independently verify the numbers computed by John Tromp and produce novel counts for boards up to size width + height = 14.
The computations were run on a AMD Ryzen 9 5950X 16-Core Processor with 128 GB RAM.
| height\width | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 5 | 13 | 35 | 96 | 267 | 750 | 2,118 | 6,010 | 17,120 | 48,930 | 140,243 | 402,956 |
| 2 | 3 | 18 | 116 | 741 | 4,688 | 29,737 | 189,648 | 1,216,721 | 7,844,298 | 50,780,523 | 329,842,064 | 2,148,495,091 | 14,027,829,516 |
| 3 | 4 | 58 | 869 | 12,031 | 158,911 | 2,087,325 | 27,441,956 | 362,940,958 | 4,816,325,017 | 64,137,689,503 | 856,653,299,180 | 11,470,572,730,124 | 153,906,772,806,519 |
| 4 | 5 | 179 | 6,000 | 161,029 | 3,945,711 | 94,910,577 | 2,265,792,710 | 54,233,186,631 | 1,295,362,125,552 | 30,932,968,221,097 | 738,548,749,700,312 | 17,631,656,694,578,592 | 420,788,402,285,901,824 |
| 5 | 6 | 537 | 38,310 | 1,706,255 | 69,763,700 | 2,818,972,642 | 112,829,665,923 | 4,499,431,376,127 | 178,942,601,291,926 | N/A | N/A | N/A | N/A |
| 6 | 7 | 1,571 | 235,781 | 15,835,683 | 1,044,334,437 | 69,173,028,785 | 4,531,985,219,092 | 290,433,534,225,566 | N/A | N/A | N/A | N/A | N/A |
| 7 | 8 | 4,587 | 1,417,322 | 135,385,909 | 14,171,315,454 | 1,523,281,696,228 | 161,965,120,344,045 | N/A | N/A | N/A | N/A | N/A | N/A |
| 8 | 9 | 13,343 | 8,424,616 | 1,104,642,469 | 182,795,971,462 | 31,936,554,362,084 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
| 9 | 10 | 38,943 | 49,867,996 | 8,754,703,921 | 2,284,654,770,108 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
| 10 | 11 | 113,835 | 294,664,010 | 67,916,896,758 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
| 11 | 12 | 333,745 | 1,741,288,730 | 519,325,538,608 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
| 12 | 13 | 980,684 | 10,300,852,227 | 3,928,940,117,357 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
| 13 | 14 | 2,888,780 | 61,028,884,959 | 29,499,214,177,403 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
With the compilation option FULLBDD=1 we computed that 95,124,612 BDD nodes are enough to encode all 7 x 6 Connect4 positions with the standard encoding.
This is a bit more than the 84,088,763 number of nodes which Edelkamp, S., & Kissmann, P. claimed in
Edelkamp, S., & Kissmann, P. (2008). Symbolic classification of general two-player games.
In KI 2008: Advances in Artificial Intelligence: 31st Annual German Conference on AI, KI 2008, Kaiserslautern, Germany, September 23-26, 2008. Proceedings 31 (pp. 185-192). Springer Berlin Heidelberg.
With the compressed encoding it only takes 59,853,336 BDD nodes.
Here are some statistics for the strong solution of the 7 x 6 board from the perspective of the first player. If ply is odd, then terminal positions are won otherwise lost for the first player.
| ply | states (won) | states (drawn) | states (lost) | states (total) | states (terminal) | nodes (won) | nodes (drawn) | nodes (lost) | nodes (total) | nodes (won+drawn+lost) |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 0 | 0 | 1 | 0 | 52 | 1 | 1 | 1 | 54 |
| 1 | 1 | 2 | 4 | 7 | 0 | 52 | 66 | 94 | 94 | 212 |
| 2 | 27 | 12 | 10 | 49 | 0 | 263 | 152 | 169 | 188 | 584 |
| 3 | 35 | 58 | 145 | 238 | 0 | 413 | 409 | 560 | 297 | 1,382 |
| 4 | 690 | 200 | 230 | 1,120 | 0 | 1,316 | 1,159 | 1,046 | 531 | 3,521 |
| 5 | 1,080 | 697 | 2,486 | 4,263 | 0 | 2,815 | 2,916 | 3,410 | 888 | 9,141 |
| 6 | 10,889 | 1,943 | 3,590 | 16,422 | 0 | 8,022 | 6,467 | 6,333 | 1,537 | 20,822 |
| 7 | 17,507 | 5,944 | 31,408 | 54,859 | 728 | 17,287 | 15,373 | 19,432 | 2,546 | 52,092 |
| 8 | 124,624 | 14,676 | 44,975 | 184,275 | 1,892 | 44,790 | 32,494 | 37,598 | 5,081 | 114,882 |
| 9 | 197,749 | 42,896 | 317,541 | 558,186 | 19,412 | 96,605 | 75,634 | 103,390 | 8,144 | 275,629 |
| 10 | 1,122,696 | 97,532 | 442,395 | 1,662,623 | 44,225 | 225,472 | 151,745 | 196,692 | 15,481 | 573,909 |
| 11 | 1,734,122 | 255,780 | 2,578,781 | 4,568,683 | 273,261 | 473,318 | 332,980 | 494,548 | 24,888 | 1,300,846 |
| 12 | 8,191,645 | 541,825 | 3,502,631 | 12,236,101 | 573,323 | 1,005,142 | 632,233 | 908,352 | 41,577 | 2,545,727 |
| 13 | 12,333,735 | 1,286,746 | 17,308,630 | 30,929,111 | 2,720,636 | 2,009,688 | 1,299,574 | 2,084,118 | 68,726 | 5,393,380 |
| 14 | 49,756,539 | 2,583,292 | 23,097,764 | 75,437,595 | 5,349,954 | 3,910,552 | 2,349,278 | 3,653,045 | 113,229 | 9,912,875 |
| 15 | 73,263,172 | 5,596,074 | 97,682,013 | 176,541,259 | 20,975,690 | 7,341,883 | 4,488,590 | 7,605,748 | 205,450 | 19,436,221 |
| 16 | 255,117,922 | 10,681,110 | 128,792,359 | 394,591,391 | 38,918,821 | 13,074,218 | 7,678,007 | 12,587,399 | 337,797 | 33,339,624 |
| 17 | 369,230,362 | 21,226,658 | 467,761,723 | 858,218,743 | 130,632,515 | 22,817,526 | 13,520,818 | 23,655,811 | 614,419 | 59,994,155 |
| 18 | 1,112,643,249 | 38,582,237 | 612,658,408 | 1,763,883,894 | 229,031,670 | 36,961,565 | 21,706,747 | 36,621,547 | 972,846 | 95,289,859 |
| 19 | 1,589,752,959 | 70,754,712 | 1,907,752,131 | 3,568,259,802 | 670,491,437 | 59,529,067 | 35,041,660 | 61,958,941 | 1,720,046 | 156,529,668 |
| 20 | 4,132,585,341 | 122,495,056 | 2,491,075,548 | 6,746,155,945 | 1,108,210,254 | 87,231,324 | 52,350,828 | 88,954,106 | 2,580,881 | 228,536,258 |
| 21 | 5,849,074,428 | 208,240,707 | 6,616,029,910 | 12,673,345,045 | 2,858,601,535 | 129,146,009 | 77,086,251 | 135,108,055 | 4,279,667 | 341,340,315 |
| 22 | 13,031,002,559 | 342,506,047 | 8,637,315,382 | 22,010,823,988 | 4,434,627,684 | 170,154,453 | 106,020,104 | 178,871,366 | 5,996,668 | 455,045,923 |
| 23 | 18,317,405,077 | 543,074,854 | 19,402,748,258 | 38,263,228,189 | 10,130,180,393 | 231,613,852 | 141,963,009 | 243,423,743 | 9,260,656 | 617,000,604 |
| 24 | 34,623,818,387 | 845,872,717 | 25,361,122,355 | 60,830,813,459 | 14,654,767,176 | 272,893,906 | 177,820,871 | 296,421,995 | 12,082,764 | 747,136,772 |
| 25 | 48,376,711,901 | 1,256,717,558 | 47,632,685,500 | 97,266,114,959 | 29,672,303,474 | 342,815,670 | 216,077,224 | 360,753,249 | 17,366,699 | 919,646,143 |
| 26 | 76,568,242,258 | 1,846,266,966 | 62,314,059,815 | 140,728,569,039 | 39,696,898,910 | 359,305,861 | 244,685,750 | 404,232,494 | 21,043,741 | 1,008,224,105 |
| 27 | 106,274,173,915 | 2,578,399,088 | 96,436,935,052 | 205,289,508,055 | 71,042,927,249 | 419,723,892 | 269,575,051 | 439,379,836 | 28,022,290 | 1,128,678,779 |
| 28 | 138,476,323,812 | 3,567,644,646 | 126,013,643,486 | 268,057,611,944 | 86,949,129,149 | 389,020,517 | 274,939,257 | 454,821,971 | 31,799,618 | 1,118,781,745 |
| 29 | 190,301,585,678 | 4,687,144,532 | 157,638,115,456 | 352,626,845,666 | 136,563,138,602 | 427,026,716 | 274,526,874 | 440,003,517 | 39,362,941 | 1,141,557,107 |
| 30 | 199,698,237,436 | 6,071,049,190 | 204,609,218,821 | 410,378,505,447 | 150,692,335,491 | 347,160,424 | 251,906,206 | 423,584,106 | 41,997,064 | 1,022,650,736 |
| 31 | 269,818,663,336 | 7,481,813,611 | 201,906,000,786 | 479,206,477,733 | 205,243,451,746 | 362,858,636 | 228,078,024 | 362,445,054 | 48,323,733 | 953,381,714 |
| 32 | 221,858,140,210 | 9,048,082,187 | 258,000,224,786 | 488,906,447,183 | 200,299,011,722 | 256,335,118 | 188,918,083 | 328,791,220 | 48,313,654 | 774,044,421 |
| 33 | 291,549,830,422 | 10,381,952,902 | 194,705,107,378 | 496,636,890,702 | 232,494,602,432 | 260,638,140 | 155,709,824 | 246,069,381 | 51,955,012 | 662,417,345 |
| 34 | 180,530,409,295 | 11,668,229,290 | 241,273,091,751 | 433,471,730,336 | 195,427,938,799 | 157,385,411 | 117,478,018 | 215,831,346 | 48,778,662 | 490,694,775 |
| 35 | 226,007,657,501 | 12,225,240,861 | 132,714,989,361 | 370,947,887,723 | 188,065,840,647 | 160,409,773 | 88,337,570 | 137,452,152 | 48,332,633 | 386,199,495 |
| 36 | 98,839,977,654 | 12,431,825,174 | 155,042,098,394 | 266,313,901,222 | 131,014,104,050 | 80,553,314 | 60,594,699 | 120,050,832 | 41,909,940 | 261,198,845 |
| 37 | 114,359,332,473 | 11,509,102,126 | 57,747,247,782 | 183,615,682,381 | 100,184,819,358 | 83,790,619 | 41,853,456 | 61,427,031 | 37,278,648 | 187,071,106 |
| 38 | 32,161,409,500 | 10,220,085,105 | 61,622,970,744 | 104,004,465,349 | 54,716,901,301 | 32,744,625 | 24,888,904 | 53,915,478 | 28,733,225 | 111,549,007 |
| 39 | 33,666,235,957 | 7,792,641,079 | 13,697,133,737 | 55,156,010,773 | 31,270,711,562 | 33,780,894 | 15,576,173 | 20,081,029 | 22,165,837 | 69,438,096 |
| 40 | 4,831,822,472 | 5,153,271,363 | 12,710,802,660 | 22,695,896,495 | 11,972,173,842 | 9,782,495 | 7,772,258 | 17,351,899 | 14,031,885 | 34,906,652 |
| 41 | 4,282,128,782 | 2,496,557,393 | 1,033,139,763 | 7,811,825,938 | 4,282,128,782 | 7,938,927 | 3,499,912 | 4,036,774 | 7,901,773 | 15,475,613 |
| 42 | 0 | 713,298,878 | 746,034,021 | 1,459,332,899 | 746,034,021 | 1 | 1,783,048 | 2,731,785 | 2,777,005 | 4,514,834 |
states (won) 2,317,028,267,398
states (drawn) 123,343,183,724
states (lost) 2,091,613,767,970
states (total) 4,531,985,219,092