Mastermind Solver
stefanabikaram.com
stefanabikaram.com
The random number generator on the old Sparcstations we were using used the time_t and PID as a seed, and the exercise test harness used a random number straight from rand(3) to generate the secret. The student's implementation simply worked out the answer from the date and process PID and knowledge of how the linear congruential generator worked.
The bitcoin theft incident.
The presumed genesis of the attack is as follows: Bitcoin operates a public database of all transactions, the block chain. Each transaction is cryptographically signed by its initiator using the ECDSA scheme. Creating ECDSA signatures requires a per-transaction nonce. Partial predictability of nonces allows for one class of attacks, but using the same nonce for two transactions signed by the same key---which is what probably happened---constitutes a catastrophic security failure. Anyone can easily identify this case from the information recorded in the block chain, reconstruct the victim’s private key, and divert their money to a bitcoin address of choice. No intrusion into the victim’s system is necessary. A loss of seed entropy in the PRNG used for generating nonces increases the probability of the breach.
This ultimately caused Google to replace the Android PRNG, which was only providing 40% of the requested entropy for this particular use.
-----
I also recall someone had discovered an online poker service's shuffle algorithm was something common (maybe Fisher-Yates?) based on java.util.Random with system time as a seed. Once you see your own hand and part of the board, you get a fairly accurate idea of what could be in someone else's hand from the small-ish sample of possible deals around system time.
Found it:
https://algs4.cs.princeton.edu/lectures/keynote/21Elementary...
Slide 61-62, has some more references.
> The computer as Master Mind. Journal of Recreational Mathematics 9 (1976), pp. 1–6. Reprinted with an addendum as Chapter 25 of Selected Papers on Fun and Games.
https://www.cs.uni.edu/~wallingf/teaching/cs3530/resources/k... is the original paper, but the addendum in the 2011 book is 5 pages long, longer than the original article itself (not counting its figure/table), and discusses various later results/ideas. For example, the addendum mentions that while Knuth's approach only minimizes the worst-case number of guesses (5, with 4.4753 on average), we can try to also minimize the expected number of guesses: I don't want to quote at length, but see https://stackoverflow.com/a/54917672 and the discussion on the German Wikipedia https://de.wikipedia.org/w/index.php?title=Mastermind_(Spiel....
In this post however, it looks like
• the scoring function is wrong, as pointed out in comment here by danbruc,
• the code in the post simply guesses at random ("It only randomly selects its next guess from a pool of possible remaining guessing"), while Knuth's approach is of "choosing at every stage a test pattern that minimizes the maximum number of remaining possibilities over all conceivable responses by the codemaker". (In fact if you run the program in the post, it takes 6 guesses to win!)
if guess[i] == secret_code[i]:
red += 1
else:
if guess[i] in secret_code:
white += 1
With the secret XXXY a guess of YYYY will be scored as one red and three white but it should be just one red. You have to keep track of which positions in the secret have already been consumed, in this case the Y in the secret gets consumed by the Y in the same position in the guess yielding the one red, for the guess YYYX the Y in the secret will be consumed by the first Y in the guess yielding only one white instead of three. Plus of course a second white for the X. When implementing this, one has to be careful that a white does not consume a later red if one wants to do it in a single loop, i.e. it is not good enough to look for any unconsumed match, it must be unconsumed and also not yield a red. from collections import Counter
def score_guess(guess, secret_code):
red = sum(guess[i] == secret_code[i] for i in range(len(guess)))
total = (Counter(guess) & Counter(secret_code)).total()
return (red, total - red)
assert score_guess('YYYY', 'XXXY') == (1, 0)
assert score_guess('YYYX', 'XXXY') == (0, 2)
(The `&` on `Counter` computes minimum of the two counts.)Here is my Rust version with multi-threading, SIMD, WASM running on your device inside a WebApp: https://0xbe7a.github.io/mastermind/
Repo: https://github.com/0xbe7a/mastermind
It is quite fast (1.8 Billion position pairs evaluated in 1652ms on my device) and can also exploit some symmetries inside the solution space.
char*p,*q,*r,s[50000];int i,j,k,l;main(){for(r=s,i=10000;i--;r++)for(j=i+3211,k=4;k--;*r++=48+j%10,j/=10);for(;puts((k=r-s)?q=r-5:"?"),k&&k-5;)for (scanf("%d",&k),p=s;p-r;){for(i=j=0;j-16;p[j%4]|=l=!(p[j%4]-q[j/4])?i++,64:0,j|=l/17,j++);for(j=0;j-5;i+=!((*p++&=63)-q[j++])*9);for(;k-i+9&&j--;*--p=*--r);}}
You think of a 4 digit number, the code tries to guess it. You give it 2 digit answers for red and white.Click "Solve within 5 moves"
Gory details here: https://github.com/ludi317/ludi317.github.io
I also have a copy of Mastermind that my parents had as kids, it's probably from the 70s? Cool vibes all around, HN. :-)