9
\$\begingroup\$

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.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ I think this spec would be improved by not having people go read the actual specification of the game in another challenge. \$\endgroup\$ Commented Jan 20, 2016 at 16:58

8 Answers 8

5
\$\begingroup\$

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
\$\endgroup\$
2
  • \$\begingroup\$ 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" \$\endgroup\$
    – tmartin
    Commented Sep 26, 2013 at 10:00
  • \$\begingroup\$ Thanks, you are entirely right. Adds 3 characters ~.@ \$\endgroup\$
    – jpjacobs
    Commented Sep 27, 2013 at 7:56
4
\$\begingroup\$

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
\$\endgroup\$
2
\$\begingroup\$

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]
\$\endgroup\$
4
  • \$\begingroup\$ Modifying the parameters is fine (they're being passed by value, not by reference). \$\endgroup\$
    – Joe Z.
    Commented Sep 9, 2013 at 5:28
  • \$\begingroup\$ @JoeZ. Why is this the accepted answer, when there are at least three other answers that are shorter? \$\endgroup\$
    – user8777
    Commented Sep 9, 2013 at 23:16
  • \$\begingroup\$ It was just one of the first to be posted. I'm sure he'll update it next time he checks :> \$\endgroup\$ Commented Sep 9, 2013 at 23:52
  • \$\begingroup\$ @chron: Yup. ` ` \$\endgroup\$
    – Joe Z.
    Commented Sep 10, 2013 at 0:31
1
\$\begingroup\$

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]
\$\endgroup\$
1
\$\begingroup\$

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

\$\endgroup\$
1
\$\begingroup\$

MATL, 17 bytes

=s,4Y2@G!=s]Xlsy-

Try it online (Tested multiple inputs with this)

Explanation:

(For white pegs, Knuth's definition as mentioned by Peter Taylor is used: sum_{i=0}^{9} min(count(secret, i), count(guess, i)))

=         % check which digits are equal
s         % sum of 1s is the number of red pegs
,         % do twice
  4Y2     % predefined literal: '0':'9'
  @G      % get the next input
  !=      % broadcast equality check
  s       % sum to get the count of each digit in the input
]         % end
Xl        % elementwise minimum
s         % sum
y-        % subtract the number of red pegs
          % (implicit) convert to string and display
\$\endgroup\$
0
\$\begingroup\$

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)
\$\endgroup\$
3
  • \$\begingroup\$ The use of set makes me suspect that it will give a wrong answer for m("0011", "1100") (should be (0,4)). \$\endgroup\$ Commented Sep 9, 2013 at 7:25
  • \$\begingroup\$ @PeterTaylor Probably, but the rules seemed really poorly defined. But I'll change my anaswer if its wrong. \$\endgroup\$
    – user8777
    Commented Sep 9, 2013 at 12:34
  • 1
    \$\begingroup\$ Knuth 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". \$\endgroup\$ Commented Sep 9, 2013 at 12:38
0
\$\begingroup\$

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
}
\$\endgroup\$
1
  • \$\begingroup\$ Zeroes are allowed, so "0" shouldn't be the padding character. \$\endgroup\$
    – Joe Z.
    Commented Sep 12, 2013 at 17:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.