-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdice-solver3x3.py
More file actions
175 lines (148 loc) · 5.77 KB
/
Copy pathdice-solver3x3.py
File metadata and controls
175 lines (148 loc) · 5.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
from itertools import combinations
def generate_matrices_6_to_3():
"""
Generate all 3x3 matrices (represented as a list of length 9)
that have exactly six +1 entries and three -1 entries.
We'll store the matrix in row-major order:
index 0 = (row=0, col=0)
index 1 = (row=0, col=1)
...
index 8 = (row=2, col=2)
"""
all_indices = range(9)
# Choose which 6 positions will be +1, the remaining 3 will be -1.
for plus_ones_positions in combinations(all_indices, 5):
# Create a list of length 9, default -1
matrix = [-1]*9
for pos in plus_ones_positions:
matrix[pos] = 1
yield matrix
def matrix_entry(matrix, r, c):
"""
Helper to access the entry of a 3x3 list 'matrix' in row r, col c.
matrix is stored in row-major form, so index = r*3 + c.
"""
return matrix[r*3 + c]
def has_forbidden_3_cycle(M_AB, M_BC, M_CA):
"""
Check if the given trio of 3x3 matrices (each stored in a length-9 list)
produces a forbidden 3-cycle. A forbidden 3-cycle means:
- For some (i, j, k) in {0,1,2}^3, the sum of
[M_AB[i,j], M_BC[j,k], M_CA[k,i]] = 3 or -3
- i.e. they are all +1 or all -1.
Return True if there *is* a forbidden 3-cycle, otherwise False.
"""
for i in range(3): # i indexes A
for j in range(3): # j indexes B
for k in range(3): # k indexes C
val_ab = matrix_entry(M_AB, i, j)
val_bc = matrix_entry(M_BC, j, k)
val_ca = matrix_entry(M_CA, k, i)
cycle_sum = val_ab + val_bc + val_ca
if cycle_sum == 3 or cycle_sum == -3:
# Found a forbidden 3-cycle
return True
return False
def print_matrix(m):
"""
Print a 3x3 matrix (stored in a 9-element list) in a more readable format.
"""
for r in range(3):
row_vals = [matrix_entry(m, r, c) for c in range(3)]
print(row_vals)
print()
def count_orderings(M_AB, M_BC, M_CA):
"""
Given three flattened 3x3 matrices M_AB, M_BC, M_CA,
return a dictionary showing how many of the 27 possible
(i, j, k) triples produce each of the 6 orderings:
- "A>B>C"
- "A>C>B"
- "B>A>C"
- "B>C>A"
- "C>A>B"
- "C>B>A"
"""
# Initialize a dictionary to hold the count of each ordering.
ordering_counts = {
"A>B>C": 0,
"A>C>B": 0,
"B>A>C": 0,
"B>C>A": 0,
"C>A>B": 0,
"C>B>A": 0,
}
# Enumerate all triples (i, j, k) in {0,1,2}^3.
for i in range(3):
for j in range(3):
for k in range(3):
# Tally wins for each of A, B, C in this triple.
wins = {"A": 0, "B": 0, "C": 0}
# A_i vs B_j
if matrix_entry(M_AB, i, j) == 1:
wins["A"] += 1
else:
wins["B"] += 1
# B_j vs C_k
if matrix_entry(M_BC, j, k) == 1:
wins["B"] += 1
else:
wins["C"] += 1
# C_k vs A_i
if matrix_entry(M_CA, k, i) == 1:
wins["C"] += 1
else:
wins["A"] += 1
# Figure out first/second/third place
# We expect the wins distribution to be (2,1,0) with no ties
# (assuming no 3-cycles).
sorted_results = sorted(
[(wins["A"], "A"), (wins["B"], "B"), (wins["C"], "C")],
key=lambda x: x[0],
reverse=True
)
# The ordering is like "X>Y>Z"
first = sorted_results[0][1]
second = sorted_results[1][1]
third = sorted_results[2][1]
ordering_str = f"{first}>{second}>{third}"
ordering_counts[ordering_str] += 1
return ordering_counts
def main():
# Step 1: Generate all valid 3x3 matrices (six +1, three -1).
matrices_AB = list(generate_matrices_6_to_3())
matrices_BC = list(generate_matrices_6_to_3())
matrices_CA = list(generate_matrices_6_to_3())
solutions_checked = 0
solutions_found = 0
valid_solutions = []
# Step 2: Iterate through all combinations of M_AB, M_BC, M_CA
for M_AB in matrices_AB:
for M_BC in matrices_BC:
for M_CA in matrices_CA:
# Step 3: Check the 3-cycle condition
solutions_checked += 1
if not has_forbidden_3_cycle(M_AB, M_BC, M_CA):
# We found a valid configuration
solutions_found += 1
print(f"Solution #{solutions_found}")
print("M_AB:")
print_matrix(M_AB)
print("M_BC:")
print_matrix(M_BC)
print("M_CA:")
print_matrix(M_CA)
print("Orderings:")
ordering_counts = count_orderings(M_AB, M_BC, M_CA)
for ordering, count in ordering_counts.items():
print(f" {ordering}: {count}")
orderings_set = set(ordering_counts.values())
print("Unique orderings:", orderings_set)
if len(orderings_set) <= 2:
valid_solutions.append(orderings_set)
print("="*40)
print(f"Total solutions checked: {solutions_checked}")
print(f"Total valid solutions found: {solutions_found}")
print(f"Total valid solutions with 2 unique orderings: {valid_solutions}")
if __name__ == "__main__":
main()