Build a Mastermind engine

6

2

For reference to the game of Mastermind: Solving Mastermind in 6 or less moves

In the version of Mastermind used in this problem, the operating string is a 4-digit number, from 0000 to 9999, represented as either numbers from 0 to 9999 or strings of consecutive digits "0000" to "9999" in input.

Your task is to build a function that will take two numbers/strings secret and guess as input, and return two numbers A and B, the number of red and white pegs respectively.

The shortest code in any language to do this wins.

Joe Z.

Posted 2013-09-08T19:05:50.050

Reputation: 30 589

2I think this spec would be improved by not having people go read the actual specification of the game in another challenge. – Martin Ender – 2016-01-20T16:58:08.923

Answers

5

J: 40 26 29 22

=,&(+/)(~:#[)e.&~.~:#]
  • change: don't laminate, but work truly dyadically, eliminating need for explicit ranks ("1), lamination(,:) and between (/)
  • change: don't count duplicate characters twice
  • change: Strings are more efficient as they don't need parsing

This verb takes a left and right argument a string from '0000' to '9999'. It takes care of doubles and excludes exact matches from checking correct numbers but wrong place.

Test cases:

NB. Give the beast a name
mm=: =,&(+/)(~:#[)e.&~.~:#]
NB. make 6 by 4 arrays of the inputs
secret =: (, ;. _2) '1254 1234 5441 5441 5441 5441 '
guess  =: (, ;. _2) '1342 1111 1234 4531 4441 5441 '

NB. Apply the verb on each pair of secret/guess
'1111' mm '1231'
2 0

NB. A whole bunch of test cases with formatting and printing of the inputs:
secret ([,' ',(":@mm),' ',])"1 guess

NB. output:
1254 1 2 1342
1234 1 0 1111
5441 0 2 1234
5441 1 2 4531
5441 3 0 4441
5441 4 0 5441

A little bit of explanation, reading from right to left:

NB.   ExactMatch: checks where digits correspond:
ExactMatch =: =

NB.   GoodDigitWrongPlace: Copies non-matched numbers from both arguments (left and right
NB.   pairs of parentheses, and checks them for same elements(e.), after eliminating
NB.   doubles in both (&~.)
GoodDigitWrongPlace =: (~: # [) (e.&~.) (~: # ])

NB.   Piecing the conditions together, after summing the booleans:
mm =: ExactMatch ,&(+/) GoodDigitWrongPlace

jpjacobs

Posted 2013-09-08T19:05:50.050

Reputation: 3 440

I think the result of "5441" and "1234" should be 0,2 rather than 0,3. The duplicate 4's should only be counted once. Save for "5441","4531" – tmartin – 2013-09-26T10:00:49.930

Thanks, you are entirely right. Adds 3 characters ~.@ – jpjacobs – 2013-09-27T07:56:32.623

3

K, 41 36

{+/'(b;(in).(?x;y)@\:(!#x)@&~b:x=y)}

.

k)c1:("1254";"1234";"5441";"5441";"5441";"5441")
k)c2:("1342";"1111";"1234";"4531";"4441";"5441")
k){+/'(b;(in).(?x;y)@\:(!#x)@&~b:x=y)}'[c1;c2]
1 2
1 0
0 2
1 2
3 0
4 0

tmartin

Posted 2013-09-08T19:05:50.050

Reputation: 3 917

2

Ruby 2.0, 71 characters

f=->s,g{[x=(0..3).count{|n|s[n]==g[n]},g.chars.count{|e|s.sub!e,''}-x]}

This function actually modifies the first parameter, which is obviously a bit dodgy. Would be easy enough to fix if it's actually against the rules.

I'm sure there's something sneaky you could do with String#count but I couldn't think of a nice way to handle duplicates.

Example usage:

p f['1254', '1342'] #=> [1, 2]
p f['1234', '1111'] #=> [1, 0]
p f['5441', '1234'] #=> [0, 2]
p f['5441', '4531'] #=> [1, 2]
p f['5441', '4441'] #=> [3, 0]
p f['5441', '5441'] #=> [4, 0]

Paul Prestidge

Posted 2013-09-08T19:05:50.050

Reputation: 2 390

Modifying the parameters is fine (they're being passed by value, not by reference). – Joe Z. – 2013-09-09T05:28:15.243

@JoeZ. Why is this the accepted answer, when there are at least three other answers that are shorter? – None – 2013-09-09T23:16:13.050

It was just one of the first to be posted. I'm sure he'll update it next time he checks :> – Paul Prestidge – 2013-09-09T23:52:29.970

@chron: Yup. – Joe Z. – 2013-09-10T00:31:39.510

1

GolfScript, 50 characters

{[[\].zip{1/~=},,\{$}/{1$?.)!!{)>0}*)},,\;1$-]}:M;

A similar solution to chron's answer written in GolfScript. Input must be provided as two strings on the stack, the result will be the array [A B].

Examples:

"1254" "1342" M p    # => [1 2]
"1234" "1111" M p    # => [1 0]
"5441" "1234" M p    # => [0 2]
"5441" "4531" M p    # => [1 2]
"5441" "4441" M p    # => [3 0]
"5441" "5441" M p    # => [4 0]

Howard

Posted 2013-09-08T19:05:50.050

Reputation: 23 109

1

GolfScript (48 chars)

{[\]:&zip{1/~=},,.~)10,{{''+-,~5+}+&%.~>=+}/}:E;

E here stands for eval. I take a similar approach to Knuth's definition of the number of white pegs by calculating sum_{i=0}^{9} min(count(secret, i), count(guess, i)).

Online demo

Peter Taylor

Posted 2013-09-08T19:05:50.050

Reputation: 41 901

0

Python - 93

I'm pretty sure this is right. But a few example input/outputs in the question would help.

def m(S,G):
 R = [(s,g) for s,g in zip(S,G) if s!=g];S,G=zip(*R);W=set(S)&set(G);return 4-len(R),len(W)

And calling it gives:

print m('1234','1231') 
>>> (3, 0)
print m('1234','1230') 
>>> (3, 0)
print m('1234','1243') 
>>> (2, 2)
print m('1234','4312') 
>>> (0, 4)
print m("0123","0131") 
>>> (2, 1)
print m("0123","0132") 
>>> (2, 2)
print m("0123","0123") 
>>> (4, 0)
print m("0123","4131") 
>>> (1, 1)

user8777

Posted 2013-09-08T19:05:50.050

Reputation:

The use of set makes me suspect that it will give a wrong answer for m("0011", "1100") (should be (0,4)). – Peter Taylor – 2013-09-09T07:25:36.717

@PeterTaylor Probably, but the rules seemed really poorly defined. But I'll change my anaswer if its wrong. – None – 2013-09-09T12:34:20.943

1Knuth himself says that the rule for the number of white pegs "is somewhat difficult to state precisely and unambiguously, and the manufacturers have not succeeded in doing so on the directions they furnish with the game". – Peter Taylor – 2013-09-09T12:38:21.333

0

Here's a first draft in PowerShell: 168 characters.

function Test-Mastermind{param([char[]]$s,[char[]]$g)
4-($p=0..3|?{$s[$_]-ne$g[$_]}).Count
($p|%{$i=$_;$p|?{$s[$i]-eq$g[$_]-and0-ne$s[$i]}|%{($s[$i]=$g[$_]=0)}}).Count}

For the sake of illumination:

function Test-Mastermind {
    param([char[]]$s,[char[]]$g)
    4 - ($p = 0..3|?{ $s[$_] -ne $g[$_] }).count # count same  characters
    ( $p | % { $i = $_                           # ForEach $i in $p (char in secret)
        $p | ? {                                 # ForEach $_ in $p (char in guess) 
            $s[$i] -eq $g[$_] -and 0 -ne $s[$i]  # If they match and aren't 0
         } | % { ($s[$i]=$g[$_]=0) }             # 0 them out, output something we can count
    } ).Count
}

Jaykul

Posted 2013-09-08T19:05:50.050

Reputation: 223

Zeroes are allowed, so "0" shouldn't be the padding character. – Joe Z. – 2013-09-12T17:41:25.570