-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathpunchout-js.cc
More file actions
74 lines (68 loc) · 3.08 KB
/
punchout-js.cc
File metadata and controls
74 lines (68 loc) · 3.08 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
#include <algorithm>
#include <cinttypes>
#include <cstdint>
#include <cstdio>
#include <vector>
#include <string>
#include <mutex>
#include <map>
static const signed char hash[] = {6,3,5,7,9, 0,1,4,2,8}, offs[] = {1,2,0,-1, 2,0,1,-1, 0,1,2,-1, -1,-1,-1,-1};
/* For best performance, use this command to compile this program:
g++ punchout-js.cc -std=c++0x -Wall -Wextra -Ofast -fopenmp -march=native
*/
int main()
{
std::map<std::string, std::vector<std::uint_fast64_t>> stats;
std::mutex lk;
#pragma omp parallel for
for(std::uint_fast64_t pass=0; pass <= 9999999999u; ++pass)
{
// Convert password into an array of digits
unsigned char p[10];
{ auto v=pass; for(unsigned n=10; n--; ) { p[n]=v%10; v/=10; } }
// Undo the first round of encryption: Substitution cipher
for(unsigned n=0; n<10; ++n) if( (signed char)(p[n] -= hash[n]) < 0 ) p[n] += 10;
// Undo the second round of encryption: Bit rotation
signed char roll = p[9] & 3;
for(unsigned r = roll; r--; )
{
for(unsigned n=9; n>=1; --n)
p[n] = (p[n] >> 1) | ((p[n-1] & 1) << 2);
p[0] = (p[0] >> 1) | ((p[9] & 2) << 1);
}
// Combine the ten 3-bit units into five 6-bit units
for(unsigned n=0; n<5; ++n) p[n] = (p[n*2] << 3) | p[n*2+1];
// Data extraction and range checking
unsigned r0 = (p[0] & 0xC) | (p[1] & 3); if(r0 >= 10) continue;
unsigned r1 = (p[3] & 0xC) | (p[2] & 3); if(r1 >= 10) continue;
unsigned r3 = (p[4] >> 4) & 3;
unsigned r4 = (p[2] & 0xC) | (p[3] & 3); if(r4 >= 10) continue;
unsigned r5 = (p[1] & 0xC) | (p[0] & 3); if(r5 >= 10) continue;
unsigned r6 = (p[4] >> 2) & 3; if(roll != offs[r6*4 + r3]) continue; // This verifies roll and also ensures r6<3 and r3<3.
// Checksum validation
if(((r0+r1+r3+r4+r5) ^ 0xFF)
!= ((p[0] & 0x30) | ((p[3] & 0x30) << 2) | ((p[1] & 0x30) >> 2) | ((p[2] & 0x30) >> 4))) continue;
// Verify that the number of wins is at least 3,
// and that the number of KOs is not larger than the number of wins.
// This check doesn't quite work right when number of wins < 10, but it's exactly what the game does.
unsigned A = r0;
if(A == 0) { A = r1; if(A < 3) continue; }
if(A < r4 || (A == r4 && r1 < r5)) continue;
char name[64]; std::sprintf(name, "win=%d%d lose=%d ko=%d%d circuit=%d", r0,r1, r3, r4,r5, r6);
std::lock_guard<std::mutex> l(lk);
stats[name].push_back(pass);
if(stats.size()%16 == 0) std::fprintf(stderr, "%" PRIuFAST64 "\r", pass);
}
std::fprintf(stderr, "\n");
std::size_t totalpass=0;
for(auto& k: stats)
{
std::printf("%s", k.first.c_str());
std::sort(k.second.begin(), k.second.end()); // Sorting makes sure the output is the same each time
for(auto pass: k.second)
std::printf(" %010" PRIuFAST64, pass);
std::printf("\n");
totalpass += k.second.size();
}
std::printf("%zu settings, %zu passwords\n", stats.size(), totalpass);
}