-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathverify-minimized.cc
153 lines (138 loc) · 5.3 KB
/
verify-minimized.cc
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
// Verifies the correctness of minimized.bin completely.
//
// Sadly, this is quite slow. (Might take in the order of 100 days to verify
// everything.)
#include "board.h"
#include "chunks.h"
#include "macros.h"
#include "minimized-accessor.h"
#include "minimized-lookup.h"
#include "parse-int.h"
#include "perms.h"
#include "position-value.h"
#include "search.h"
#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <thread>
namespace {
// Number of threads to use for calculations. 0 to disable multithreading.
// By default, use 1.5 times the number of cores.
const int thread_count = (std::thread::hardware_concurrency() * 3 + 1) / 2;
// Create a checkpoint after this many iterations.
const int64_t checkpoint_interval = 100000;
void VerifyThread(
MinimizedAccessor *acc,
int64_t end_index,
std::atomic<int64_t> *next_index,
std::atomic<int64_t> *failures,
std::mutex *io_mutex) {
std::vector<int64_t> offsets_scratch;
std::vector<uint8_t> bytes_scratch;
int64_t index;
while ((index = (*next_index)++) < end_index) {
Perm perm = PermAtMinIndex(index);
uint8_t actual_byte = acc->ReadByte(index);
uint8_t expected_byte;
// Special case for when we expect a win-in-1 (about 84.9% of minimized
// positions): quickly confirm that there is an immediate winning turn,
// instead of enumerating all successors with RecalculateValue().
if (actual_byte == Value::WinIn(1).byte && (PartialHasWinningMove(perm) || HasWinningMove(perm))) {
expected_byte = Value::WinIn(1).byte;
} else {
// General case: recalculate the value of perm from its successors.
expected_byte = RecalculateValue(*acc, perm, offsets_scratch, bytes_scratch).byte;
}
if (expected_byte != actual_byte) [[unlikely]] {
std::lock_guard lock(*io_mutex);
std::cerr << "FAILURE at min-index +" << index << "! "
<< "expected: " << int{expected_byte} << " (" << Value(expected_byte) << "); "
<< "actual: " << int{actual_byte} << " (" << Value(actual_byte) << ")." << std::endl;
++(*failures);
}
}
}
int64_t Verify(MinimizedAccessor &acc, int64_t start_index, int64_t end_index) {
std::atomic<int64_t> next_index = start_index;
std::atomic<int64_t> failures = 0;
std::mutex io_mutex;
if (thread_count == 0) {
VerifyThread(&acc, end_index, &next_index, &failures, &io_mutex);
assert(next_index == end_index + 1);
} else {
std::vector<std::thread> threads;
threads.reserve(thread_count);
REP(i, thread_count) {
threads.emplace_back(
VerifyThread, &acc, end_index, &next_index, &failures, &io_mutex);
}
REP(i, thread_count) {
threads[i].join();
}
assert(next_index == end_index + thread_count);
}
return failures;
}
} // namespace
int main(int argc, char *argv[]) {
if (argc < 3 || argc > 5) {
std::cerr << "Usage: expand-minimized <minimized.bin> <checkpoint-file> [<start-index> [<end-index>]]" << std::endl;
return EXIT_FAILURE;
}
const char *minimized_path = argv[1];
const char *checkpoint_path = argv[2];
int64_t start_index = 0;
int64_t end_index = min_index_size;
if (argc > 3) {
start_index = ParseInt64(argv[3]);
if (start_index < 0 || start_index >= min_index_size) {
std::cerr << "Invalid start index: " << argv[3] << std::endl;
return EXIT_FAILURE;
}
if (argc > 4) {
end_index = ParseInt64(argv[4]);
if (end_index < start_index || end_index > min_index_size) {
std::cerr << "Invalid end index: " << argv[4] << std::endl;
return EXIT_FAILURE;
}
}
}
std::cerr << "Solving from min-index +" << start_index << " to min-index +" << end_index
<< " using " << thread_count << " threads." << std::endl;
int64_t checkpoint_index = 0;
std::ifstream ifs(checkpoint_path);
if (!ifs || !(ifs >> checkpoint_index)) {
std::cerr << "Could not read previous checkpoint index! "
<< "Restarting computation from min-index +" << start_index << "." << std::endl;
checkpoint_index = start_index;
} else if (checkpoint_index < start_index || checkpoint_index > end_index) {
std::cerr << "Checkpoint index (" << checkpoint_index << ") out of range!" << std::endl;
return EXIT_FAILURE;
} else {
std::cerr << "Resuming from checkpoint min-index +" << checkpoint_index << std::endl;
}
MinimizedAccessor acc(minimized_path);
while (checkpoint_index < end_index) {
int64_t chunk_start_index = checkpoint_index;
int64_t chunk_end_index = std::min(checkpoint_index + checkpoint_interval, end_index);
int64_t failures = Verify(acc, chunk_start_index, chunk_end_index);
if (failures != 0) {
std::cerr << "Verification failures detected!" << std::endl;
return EXIT_FAILURE;
}
checkpoint_index = chunk_end_index;
std::ofstream ofs(checkpoint_path);
if (!ofs || !(ofs << checkpoint_index << std::endl)) {
std::cerr << "Failed to write checkpoint index!";
return EXIT_FAILURE;
}
double percent_complete = 100.0 * (checkpoint_index - start_index) / (end_index - start_index);
std::cerr << "Wrote checkpoint at index " << checkpoint_index
<< " (" << std::fixed << percent_complete << "% done)" << std::endl;
}
std::cerr << "Verification complete!" << std::endl;
return EXIT_SUCCESS;
}