Mastermind

From Rosetta Code
Mastermind is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Create a simple version of the board game: Mastermind.

It must be possible to:

  • choose the number of colors will be used in the game(2 - 20)
  • choose the color code length(4 - 10)
  • choose the maximum number of guesses the player has (7 - 20)
  • choose whether or not will be repeated colors in the code


The game should display all the player guesses and the results of that guess.

Display(just an idea.):

Feature Graphic Version Text Version
Player guess Colored circles Alphabet letters
Correct color & position Black circle X
Correct color White circle O
None Gray circle -

A text version example: 1: ADEF - XXO-
Translates to:
first guess;
the four colors(ADEF);
result: two correct colors and spot, one correct color/wrong spot one color is not in the code.

Happy coding!


Related tasks



C++[edit]

 
#include <iostream>
#include <algorithm>
#include <ctime>
#include <string>
#include <vector>
 
typedef std::vector<char> vecChar;
 
class master {
public:
master( size_t code_len, size_t clr_count, size_t guess_count, bool rpt ) {
std::string color = "ABCDEFGHIJKLMNOPQRST";
 
if( code_len < 4 ) code_len = 4; else if( code_len > 10 ) code_len = 10;
if( !rpt && clr_count < code_len ) clr_count = code_len;
if( clr_count < 2 ) clr_count = 2; else if( clr_count > 20 ) clr_count = 20;
if( guess_count < 7 ) guess_count = 7; else if( guess_count > 20 ) guess_count = 20;
 
codeLen = code_len; colorsCnt = clr_count; guessCnt = guess_count; repeatClr = rpt;
 
for( size_t s = 0; s < colorsCnt; s++ ) {
colors.append( 1, color.at( s ) );
}
}
void play() {
bool win = false;
combo = getCombo();
 
while( guessCnt ) {
showBoard();
if( checkInput( getInput() ) ) {
win = true;
break;
}
guessCnt--;
}
if( win ) {
std::cout << "\n\n--------------------------------\n" <<
"Very well done!\nYou found the code: " << combo <<
"\n--------------------------------\n\n";
} else {
std::cout << "\n\n--------------------------------\n" <<
"I am sorry, you couldn't make it!\nThe code was: " << combo <<
"\n--------------------------------\n\n";
}
}
private:
void showBoard() {
vecChar::iterator y;
for( int x = 0; x < guesses.size(); x++ ) {
std::cout << "\n--------------------------------\n";
std::cout << x + 1 << ": ";
for( y = guesses[x].begin(); y != guesses[x].end(); y++ ) {
std::cout << *y << " ";
}
 
std::cout << " : ";
for( y = results[x].begin(); y != results[x].end(); y++ ) {
std::cout << *y << " ";
}
 
int z = codeLen - results[x].size();
if( z > 0 ) {
for( int x = 0; x < z; x++ ) std::cout << "- ";
}
}
std::cout << "\n\n";
}
std::string getInput() {
std::string a;
while( true ) {
std::cout << "Enter your guess (" << colors << "): ";
a = ""; std::cin >> a;
std::transform( a.begin(), a.end(), a.begin(), ::toupper );
if( a.length() > codeLen ) a.erase( codeLen );
bool r = true;
for( std::string::iterator x = a.begin(); x != a.end(); x++ ) {
if( colors.find( *x ) == std::string.npos ) {
r = false;
break;
}
}
if( r ) break;
}
return a;
}
bool checkInput( std::string a ) {
vecChar g;
for( std::string::iterator x = a.begin(); x != a.end(); x++ ) {
g.push_back( *x );
}
guesses.push_back( g );
 
int black = 0, white = 0;
std::vector<bool> match( codeLen, false );
 
for( int b = 0; b < codeLen; b++ ) {
if( a.at( b ) == combo.at( b ) ) {
match[b] = true;
black++;
continue;
}
 
for( int w = 0; w < codeLen; w++ ) {
if( !match[b] && w != b && a.at( w ) == combo.at( b ) ) {
match[b] = true;
white++;
continue;
}
}
}
 
vecChar r;
for( int b = 0; b < black; b++ ) r.push_back( 'X' );
for( int w = 0; w < white; w++ ) r.push_back( 'O' );
results.push_back( r );
 
return ( black == codeLen );
}
std::string getCombo() {
std::string c, clr = colors;
int l, z;
 
for( size_t s = 0; s < codeLen; s++ ) {
z = rand() % ( int )clr.length();
c.append( 1, clr[z] );
if( !repeatClr ) clr.erase( z, 1 );
}
return c;
}
 
size_t codeLen, colorsCnt, guessCnt;
bool repeatClr;
std::vector<vecChar> guesses, results;
std::string colors, combo;
};
 
int main( int argc, char* argv[] ) {
srand( unsigned( time( 0 ) ) );
master m( 4, 8, 12, false );
m.play();
return 0;
}
 
Output:
Enter your guess (ABCDEFGH): gbda

--------------------------------
1: A B C D  :  X O O -
--------------------------------
2: A A A E  :  O - - -
--------------------------------
3: E E E E  :  - - - -
--------------------------------
4: B B B C  :  X - - -
--------------------------------
5: D B A F  :  X O O -
--------------------------------
6: G B D A  :  X X X -

Enter your guess (ABCDEFGH): hbda


--------------------------------
Very well done!
You found the code: HBDA
--------------------------------

Lua[edit]

Based on C++

 
math.randomseed( os.time() )
local black, white, none, code = "X", "O", "-"
local colors, codeLen, maxGuess, rept, alpha, opt = 6, 4, 10, false, "ABCDEFGHIJKLMNOPQRST", ""
local guesses, results
function createCode()
code = ""
local dic, a = ""
for i = 1, colors do
dic = dic .. alpha:sub( i, i )
end
for i = 1, codeLen do
a = math.floor( math.random( 1, #dic ) )
code = code .. dic:sub( a, a )
if not rept then
dic = dic:sub(1, a - 1 ) .. dic:sub( a + 1, #dic )
end
end
end
function checkInput( inp )
table.insert( guesses, inp )
local b, w, fnd, str = 0, 0, {}, ""
for bl = 1, codeLen do
if inp:sub( bl, bl ) == code:sub( bl, bl ) then
b = b + 1; fnd[bl] = true
else
for wh = 1, codeLen do
if nil == fnd[bl] and wh ~= bl and inp:sub( wh, wh ) == code:sub( bl, bl ) then
w = w + 1; fnd[bl] = true
end
end
end
end
for i = 1, b do str = str .. string.format( "%s ", black ) end
for i = 1, w do str = str .. string.format( "%s ", white ) end
for i = 1, 2 * codeLen - #str, 2 do str = str .. string.format( "%s ", none ) end
table.insert( results, str )
return b == codeLen
end
function play()
local err, win, r = true, false;
for j = 1, colors do opt = opt .. alpha:sub( j, j ) end
while( true ) do
createCode(); guesses, results = {}, {}
for i = 1, maxGuess do
err = true;
while( err ) do
io.write( string.format( "\n-------------------------------\nYour guess (%s)?", opt ) )
inp = io.read():upper();
if #inp == codeLen then
err = false;
for k = 1, #inp do
if( nil == opt:find( inp:sub( k, k ) ) ) then
err = true;
break;
end
end
end
end
if( checkInput( inp ) ) then win = true; break
else
for l = 1, #guesses do
print( string.format( "%.2d: %s : %s", l, guesses[l], results[l] ) )
end
end
end
if win then print( "\nWell done!" )
else print( string.format( "\nSorry, you did not crack the code --> %s!", code ) )
end
io.write( "Play again( Y/N )? " ); r = io.read()
if r ~= "Y" and r ~= "y" then break end
end
end
--[[ entry point ]]---
if arg[1] ~= nil and tonumber( arg[1] ) > 1 and tonumber( arg[1] ) < 21 then colors = tonumber( arg[1] ) end
if arg[2] ~= nil and tonumber( arg[2] ) > 3 and tonumber( arg[2] ) < 11 then codeLen = tonumber( arg[2] ) end
if arg[3] ~= nil and tonumber( arg[3] ) > 6 and tonumber( arg[3] ) < 21 then maxGuess = tonumber( arg[3] ) end
if arg[4] ~= nil and arg[4] == "true" or arg[4] == "false" then rept = ( arg[4] == "true" ) end
play()
 
Output:
-------------------------------
Your guess (ABCDEF)?bcde
01: BCDE : X X - -

-------------------------------
Your guess (ABCDEF)?bcaf
01: BCDE : X X - -
02: BCAF : X O O -

-------------------------------
Your guess (ABCDEF)?badf
01: BCDE : X X - -
02: BCAF : X O O -
03: BADF : X X O -

-------------------------------
Your guess (ABCDEF)?bafe

Well done!
Play again( Y/N )?

Perl 6[edit]

Works with: Rakudo version 2017.01

By default, plays classic Mastermind using letters in place of colors. ( 4 chosen from 6, no repeats, 10 guess limit. ) Pass in parameters to modify the game. Enter a string of --length (default 4) letters with or without spaces. Guesses accept lower or upper case.

sub MAIN (
Int :$colors where 1 < * < 21 = 6, Int :$length where 3 < * < 11 = 4,
Int :$guesses where 7 < * < 21 = 10, Bool :$repeat = False
) {
my @valid = ('A' .. 'T')[^$colors];
my $puzzle = $repeat ?? @valid.roll($length) !! @valid.pick($length);
my @guesses;
 
my $black = '●';
my $white = '○';
 
loop {
clearscr();
say header();
printf " %{$length * 2}s :: %s\n", @guesses[$_][0], @guesses[$_][1] for ^@guesses;
say '';
lose() if @guesses == $guesses;
my $guess = get-guess();
next unless $guess.&is-valid;
my $score = score($puzzle, $guess);
win() if $score eq ($black xx $length).join: ' ';
@guesses.push: [$guess, $score];
}
 
sub header {
my $num = $guesses - @guesses;
qq:to/END/;
Guess the {$length} element sequence containing the letters {@valid}
Repeats are {$repeat ?? '' !! 'not '}allowed. You have $num guess{ $num == 1 ?? '' !! 'es'} remaining.
END
}
 
sub score ($puzzle, $guess) {
my @score;
for ^$length {
if $puzzle[$_] eq $guess[$_] {
@score.push: $black;
}
elsif $puzzle[$_] eq any(@$guess) {
@score.push: $white;
}
else {
@score.push('-');
}
}
@score.sort.reverse.join: ' ';
}
 
sub clearscr { $*KERNEL ~~ /'win32'/ ?? run('cls') !! run('clear') }
 
sub get-guess { (uc prompt 'Your guess?: ').comb(/@valid/) }
 
sub is-valid (@guess) { so $length == @guess }
 
sub win { say 'You Win! The correct answer is: ', $puzzle; exit }
 
sub lose { say 'Too bad, you ran out of guesses. The solution was: ', $puzzle; exit }
}
Sample output:
Guess the 4 element sequence containing the letters A B C D E F
Repeats are not allowed. You have 5 guesses remaining.

  A B C D :: ○ ○ ○ -
  C A B E :: ● ○ ○ -
  D A E F :: ● ○ - -
  B A E C :: ● ○ ○ -
  D E B C :: ○ ○ ○ ○

Your guess?: cdeb
You Win! The correct answer is: (C D E B)

REXX[edit]

More checks could have been added   (for illegal inputs and illegal options).

/*REXX pgm scores  mastermind  game with a human  or  CBLFs (Carbon Based Life Forms).  */
parse arg let wid mxG oRep seed _ /*obtain optional arguments from the CL*/
arg . . . rep . /*get uppercase 4th argument " " " */
if let=='' | let=="," then let= 20 /*Not specified? Then use the default.*/
if wid=='' | wid=="," then wid= 4 /* " " " " " " */
if mxG=='' | mxG=="," then mxG= 20 /* " " " " " " */
if rep=='' | rep=="," then rep= 0 /* " " " " " " */
if datatype(seed,'W') then call random ,,seed /*use a seed for random repeatability. */
if abbrev( 'REPEATSALLOWED',rep,3) then rep=1 /*allow an abbreviated option for REP. */
if abbrev('NOREPEATSALLOWED',rep,3) then rep=0 /* " " " " " " */
call vet arg(), 'args' /*Vet the number of arguments entered. */ /*◄■■■■■■ optional vetting.*/
call vet let, 'letters', 2, 20 /* " " " " letters in the code*/ /*◄■■■■■■ optional vetting.*/
call vet wid, 'width', 4, 10 /* " " " " the width of code. */ /*◄■■■■■■ optional vetting.*/
call vet mxG, 'maxGuess', 7, 20 /* " " " " maximum guesses. */ /*◄■■■■■■ optional vetting.*/
call vet rep, 'REP', 0, 1e8 /* " " value if repeats are allowed*/ /*◄■■■■■■ optional vetting.*/
call gen; yourG= 'Your guess must be exactly '
youve= "You've already tried that guess "
do prompt=0 by 0 until xx==wid; say /*play until guessed or QUIT is entered*/
say id 'Please enter a guess with ' wid ' letters [or Quit]:'
pull g; g=space(g,0); L=length(g); if abbrev('QUIT',g,1) then exit 0
if L\==wid then do; say id '***error***' yourG wid " letters."; iterate; end
call dups /*look through the history log for dups*/
q=?; XX=0; OO=0; try=try+1 /*initialize some REXX vars; bump TRY.*/
 
do j=1 for L; if substr(g,j,1) \== substr(q,j,1) then iterate /*hit? */
xx=xx+1; q=overlay('▒', q, j) /*bump the XX correct count. */
end /*j*/ /* [↑] XX correct count; scrub guess.*/
 
do k=1 for L; _=substr(g, k, 1) /*process the count for "spots". */
if pos(_, q)==0 then iterate /*is this (spot) letter in the code? */
oo=oo+1; q=translate(q, , _) /*bump the OO spot count. */
end /*k*/ /* [↑] OO spot count; & scrub guess.*/
say
@.try=id right('guess' try, 11) ' ('mxG "is the max):" g '──►' ,
copies('X', xx)copies("O", oo)copies('-', wid-xx-oo)
call hist
if try==mxG then do; say; say id "you've used the maximum guesses:" mxG
say; say id "The code was: "  ?; say; exit 1
end
end /*prompt*/
say; say " ┌─────────────────────────────────────────┐"
say " │ │"
say " │ Congratulations, you've guessed it !! │"
say " │ │"
say " └─────────────────────────────────────────┘"
exit 0 /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
dups: do h=1 for try; if g\=word(@.h, 8) then iterate /*any duplicated guesses? */
say; say id youve " (guess number" h').'; iterate prompt; end /*h*/; return
/*──────────────────────────────────────────────────────────────────────────────────────*/
gen: if rep==0 then reps= 'no' /*create a literal for the prompt msg. */
else reps=
@abc= 'QWERTYUIOPASDFGHJKLZXCVBNM' /*capital letters used for random code.*/
id='────────'; try=0; [email protected]=length(@abc) /*identifier in front of msg from here.*/
 ?=
do until length(?)==wid /*gen random codes 'til there's enough.*/
r=substr(@abc, random(1, [email protected]), 1) /*generate a random letter, 1 at a time*/
if \rep & pos(r, ?)\==0 then iterate /*maybe don't allow a repeated digit.*/
 ?=? || r; if ?=='QUIT'&let==4 then ?= /*append random letter; ··· except this*/
end /*until*/ /* [↑] builds a unique N-letter code.*/
say
say id 'A random code of ' wid "letters (out of a possible " let ' letters) '
say id 'has been generated (with' reps "repeats)."
return
/*──────────────────────────────────────────────────────────────────────────────────────*/
hist: do hist=1 for try; say @.hist; end; return /*show "guess" history.*/
s: if arg(1)==1 then return ''; return "s" /*a simpler pluraizer. */
ser: say; say; say '***error***' arg(1); say; say; exit 13
/*──────────────────────────────────────────────────────────────────────────────────────*/ /*◄■■■■■■ optional vetting.*/
vet: parse arg val,?,mn,mx /*vet (validate) a specified argument. */ /*◄■■■■■■ optional vetting.*/
if ?=="args" & (val>1 | _\='') then call ser "Too many arguments specified. " _ /*◄■■■■■■ optional vetting.*/
if ?=="args" then return /*◄■■■■■■ optional vetting.*/
if \datatype(val, 'N') then call ser ? "isn't numeric: " val /*◄■■■■■■ optional vetting.*/
if \datatype(val, 'W') then call ser ? "isn't an integer: " val /*◄■■■■■■ optional vetting.*/
if val < mn then call ser ? "has a value less than " mn /*◄■■■■■■ optional vetting.*/
if val > mx then call ser ? "has a value greater than " mx /*◄■■■■■■ optional vetting.*/
if ?=='REP' & \datatype(val,W) then call ser "Value for REPEATS isn't valid: " oRep /*◄■■■■■■ optional vetting.*/
return 1

output

──────── A random code of  4 letters  (out of a possible  20  letters)
──────── has been generated   (with no repeats).

──────── Please enter a guess with  4  letters                   [or Quit]:
abcd                  ◄■■■■■■ user input

────────     guess 1   (20 is the max): ABCD ──► ----

──────── Please enter a guess with  4  letters                   [or Quit]:
efgh                  ◄■■■■■■ user input

────────     guess 1   (20 is the max): ABCD ──► ----
────────     guess 2   (20 is the max): EFGH ──► ----

──────── Please enter a guess with  4  letters                   [or Quit]:
ijkl                  ◄■■■■■■ user input

────────     guess 1   (20 is the max): ABCD ──► ----
────────     guess 2   (20 is the max): EFGH ──► ----
────────     guess 3   (20 is the max): IJKL ──► O---

──────── Please enter a guess with  4  letters                   [or Quit]:
mnop                  ◄■■■■■■ user input

     ···············································
     ·    (Some of the output has been elided.)    ·
     ···············································

──────── Please enter a guess with  4  letters                   [or Quit]:
yinp                  ◄■■■■■■ user input

────────     guess 1   (20 is the max): ABCD ──► ----
────────     guess 2   (20 is the max): EFGH ──► ----
────────     guess 3   (20 is the max): IJKL ──► O---
────────     guess 4   (20 is the max): MNOP ──► XO--
────────     guess 5   (20 is the max): WXYZ ──► O---
────────     guess 6   (20 is the max): LKHI ──► O---
────────     guess 7   (20 is the max): PONM ──► XO--
────────     guess 8   (20 is the max): ZYXW ──► O---
────────     guess 9   (20 is the max): YZWX ──► X---
────────    guess 10   (20 is the max): MOPN ──► OO--
────────    guess 11   (20 is the max): OMPN ──► OO--
────────    guess 12   (20 is the max): LKJI ──► O---
────────    guess 13   (20 is the max): JILK ──► X---
────────    guess 14   (20 is the max): YINP ──► XXXX

          ┌─────────────────────────────────────────┐
          │                                         │
          │  Congratulations, you've guessed it !!  │
          │                                         │
          └─────────────────────────────────────────┘

zkl[edit]

Translation of: C++
class MasterMind{
fcn init(code_len,guess_count){
var codeLen =code_len.max(4).min(10);
var guessCnt=guess_count.max(7).min(20);
var colors ="ABCDEFGHIJKLMNOPQRST"[0,codeLen];
}
fcn play{
guesses,win,blackWhite:=List(),False,Void;
code:=codeLen.pump(String,'wrap(_){ colors[(0).random(codeLen)] });
do(guessCnt){
str:=getInput();
win,blackWhite = checkInput(str,code);
guesses.append(T(str,blackWhite));
showBoard(guesses);
if(win) break;
}
if(win) println("--------------------------------\n",
"Very well done!\nYou found the code: ",code);
else println("--------------------------------\n",
"I am sorry, you didn't discover the code!\nThe code was: ",code);
}
fcn [private] showBoard(guesses){
foreach n,gbw in ([1..].zip(guesses)){
guess,blackWhite := gbw;
println("%2d: %s :% s %s".fmt(n,
guess.split("").concat(" "), blackWhite.split("").concat(" "),
"- "*(codeLen - blackWhite.len())));
}
}
fcn [private] getInput{
while(True){
a:=ask("Enter your guess (" + colors + "): ").toUpper()[0,codeLen];
if(not (a-colors) and a.len()>=codeLen) return(a);
}
}
fcn [private] checkInput(guess,code){
// black: guess is correct in both color and position
// white: correct color, wrong position
matched,black := guess.split("").zipWith('==,code), matched.sum(0);
// remove black from code, prepend null to make counting easy
code = L("-").extend(matched.zipWith('wrap(m,peg){ m and "-" or peg },code));
white:=0; foreach m,p in (matched.zip(guess)){
if(not m and (z:=code.find(p))){ white+=1; code[z]="-"; }
}
return(black==codeLen,"X"*black + "O"*white)
}
}(4,12).play();
Output:
Enter your guess (ABCD): abcd
 1: A B C D : X O O - 
Enter your guess (ABCD): abcc
 1: A B C D : X O O - 
 2: A B C C : O O - - 
Enter your guess (ABCD): aaad
 1: A B C D : X O O - 
 2: A B C C : O O - - 
 3: A A A D : X - - - 
Enter your guess (ABCD): bccd
 1: A B C D : X O O - 
 2: A B C C : O O - - 
 3: A A A D : X - - - 
 4: B C C D : X X O - 
Enter your guess (ABCD): dcbd
 1: A B C D : X O O - 
 2: A B C C : O O - - 
 3: A A A D : X - - - 
 4: B C C D : X X O - 
 5: D C B D : X X X X 
--------------------------------
Very well done!
You found the code: DCBD