Mastermind

From Rosetta Code
Revision as of 23:15, 31 May 2019 by rosettacode>Gerard Schildberger (added to the Puzzles category.)


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++

<lang cpp>#include <iostream>

  1. include <algorithm>
  2. include <ctime>
  3. include <string>
  4. 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> gmatch( codeLen, false );
       std::vector<bool> cmatch( codeLen, false );

       for( int i = 0; i < codeLen; i++ ) {
           if( a.at( i ) == combo.at( i ) ) {
               gmatch[i] = true;
               cmatch[i] = true;
               black++;
           }
       }

       for( int i = 0; i < codeLen; i++ ) {
           if (gmatch[i]) continue;
           for( int j = 0; j < codeLen; j++ ) {
               if (i == j || cmatch[j]) continue;
               if( a.at( i ) == combo.at( j ) ) {
                   cmatch[j] = true;
                   white++;
                   break;
               }
           }
       }
      
       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;

} </lang>

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
--------------------------------

Easyprog.online

Run it

<lang>col[] = [ 922 990 171 229 950 808 ] len code[] 4 len guess[] 4

subr init_vars

 row = 7

. func draw_rate r black white . .

 for j range 2
   for c range 2
     move c * 3 + 67.5 r * 11 + 12.6 + j * 3
     if black > 0
       color 000
       circle 1.2
       black -= 1
     elif white > 0
       color 999
       circle 1.2
       white -= 1
     else
       color 420
       circle 0.7
     .
   .
 .

. func show_code . .

 color 642
 move 27 0
 rect 46 8
 for i range 4
   move i * 8 + 33 3
   color col[code[i]]
   circle 2
 .

. func next_row . .

 for i range 4
   guess[i] = -1
 .
 for c range 4
   move c * 9 + 32 row * 11 + 14
   color 900
   circle 2
   color 420
   circle 1.7
 .

. func rate . .

 test[] = code[]
 for i range 4
   if guess[i] = test[i]
     black += 1
     test[i] = -2
     guess[i] = -1
   .
 .
 for i range 4
   for j range 4
     if guess[i] = test[j]
       white += 1
       test[j] = -2
       guess[i] = -1
     .
   .
 .
 move 64 row * 11 + 11
 color 642
 rect 9 6
 # 
 call draw_rate row black white
 row -= 1
 if black = 4
   row = -1
 .
 if row = -1
   call show_code
 else
   call next_row
 .

. func new . .

 call init_vars
 for i range 4
   code[i] = random 6
 .
 color 642
 move 15 0
 rect 70 100
 color 420
 move 27 0
 rect 46 8
 move 35 2
 color 864
 text "Mastermind"
 color 420
 move 25 10
 line 25 94
 move 75 10
 line 75 94
 for r range 8
   for c range 4
     move c * 9 + 32 r * 11 + 14
     circle 2
   .
   call draw_rate r 0 0
 .
 call next_row

.

func do_move . .

 c = trunc ((mouse_x - 27.5) / 9)
 move c * 9 + 32 row * 11 + 14
 guess[c] = (guess[c] + 1) mod 6
 color col[guess[c]]
 circle 3.2
 # 
 if guess[0] > -1 and guess[1] > -1 and guess[2] > -1 and guess[3] > -1
   move 64 row * 11 + 11
   color 900
   rect 8.6 6
   move 64.2 row * 11 + 11.2
   color 642
   rect 8.2 5.6
   color 420
   move 64.6 row * 11 + 11.6
   text "OK"
 .

. on mouse_down

 if row = -1
   call new
 elif mouse_y > row * 11 + 9 and mouse_y < row * 11 + 19
   if mouse_x > 28 and mouse_x < 63.5
     call do_move
   elif mouse_x > 65 and mouse_x < 77
     if guess[0] > -1 and guess[1] > -1 and guess[2] > -1 and guess[3] > -1
       call rate
     .
   .
 .

. textsize 5 linewidth 0.8 call new</lang>

Go

<lang Go>package main

import ( "errors" "flag" "fmt" "log" "math/rand" "strings" "time" )

func main() { log.SetPrefix("mastermind: ") log.SetFlags(0) colours := flag.Int("colours", 6, "number of colours to use (2-20)") flag.IntVar(colours, "colors", 6, "alias for colours") holes := flag.Int("holes", 4, "number of holes (the code length, 4-10)") guesses := flag.Int("guesses", 12, "number of guesses allowed (7-20)") unique := flag.Bool("unique", false, "disallow duplicate colours in the code") flag.Parse()

rand.Seed(time.Now().UnixNano()) m, err := NewMastermind(*colours, *holes, *guesses, *unique) if err != nil { log.Fatal(err) } err = m.Play() if err != nil { log.Fatal(err) } }

type mastermind struct { colours int holes int guesses int unique bool

code string past []string // history of guesses scores []string // history of scores }

func NewMastermind(colours, holes, guesses int, unique bool) (*mastermind, error) { if colours < 2 || colours > 20 { return nil, errors.New("colours must be between 2 and 20 inclusive") } if holes < 4 || holes > 10 { return nil, errors.New("holes must be between 4 and 10 inclusive") } if guesses < 7 || guesses > 20 { return nil, errors.New("guesses must be between 7 and 20 inclusive") } if unique && holes > colours { return nil, errors.New("holes must be > colours when using unique") }

return &mastermind{ colours: colours, holes: holes, guesses: guesses, unique: unique, past: make([]string, 0, guesses), scores: make([]string, 0, guesses), }, nil }

func (m *mastermind) Play() error { m.generateCode() fmt.Printf("A set of %s has been selected as the code.\n", m.describeCode(m.unique)) fmt.Printf("You have %d guesses.\n", m.guesses) for len(m.past) < m.guesses { guess, err := m.inputGuess() if err != nil { return err } fmt.Println() m.past = append(m.past, guess) str, won := m.scoreString(m.score(guess)) if won { plural := "es" if len(m.past) == 1 { plural = "" } fmt.Printf("You found the code in %d guess%s.\n", len(m.past), plural) return nil } m.scores = append(m.scores, str) m.printHistory() fmt.Println() } fmt.Printf("You are out of guesses. The code was %s.\n", m.code) return nil }

const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" const blacks = "XXXXXXXXXX" const whites = "OOOOOOOOOO" const nones = "----------"

func (m *mastermind) describeCode(unique bool) string { ustr := "" if unique { ustr = " unique" } return fmt.Sprintf("%d%s letters (from 'A' to %q)", m.holes, ustr, charset[m.colours-1], ) }

func (m *mastermind) printHistory() { for i, g := range m.past { fmt.Printf("-----%s---%[1]s--\n", nones[:m.holes]) fmt.Printf("%2d: %s : %s\n", i+1, g, m.scores[i]) } }

func (m *mastermind) generateCode() { code := make([]byte, m.holes) if m.unique { p := rand.Perm(m.colours) for i := range code { code[i] = charset[p[i]] } } else { for i := range code { code[i] = charset[rand.Intn(m.colours)] } } m.code = string(code) //log.Printf("code is %q", m.code) }

func (m *mastermind) inputGuess() (string, error) { var input string for { fmt.Printf("Enter guess #%d: ", len(m.past)+1) if _, err := fmt.Scanln(&input); err != nil { return "", err } input = strings.ToUpper(strings.TrimSpace(input)) if m.validGuess(input) { return input, nil } fmt.Printf("A guess must consist of %s.\n", m.describeCode(false)) } }

func (m *mastermind) validGuess(input string) bool { if len(input) != m.holes { return false } for i := 0; i < len(input); i++ { c := input[i] if c < 'A' || c > charset[m.colours-1] { return false } } return true }

func (m *mastermind) score(guess string) (black, white int) { scored := make([]bool, m.holes) for i := 0; i < len(guess); i++ { if guess[i] == m.code[i] { black++ scored[i] = true } } for i := 0; i < len(guess); i++ { if guess[i] == m.code[i] { continue } for j := 0; j < len(m.code); j++ { if i != j && !scored[j] && guess[i] == m.code[j] { white++ scored[j] = true } } } return }

func (m *mastermind) scoreString(black, white int) (string, bool) { none := m.holes - black - white return blacks[:black] + whites[:white] + nones[:none], black == m.holes }</lang>

Output (using Knuth's five-guess algorithm):
A set of 4 letters (from 'A' to 'F') has been selected as the code.
You have 12 guesses.
[... output of first guesses omitted ...]
Enter guess #4: FEDE

------------------
 1:  AABB : ----
------------------
 2:  FFED : XOOO
------------------
 3:  DFDF : XO--
------------------
 4:  FEDE : XOOO

Enter guess #5: EFFE

You found the code in 5 guesses.

Kotlin

Translation of: C++

<lang scala>// version 1.2.51

import java.util.Random

val rand = Random()

class Mastermind {

   private val codeLen: Int
   private val colorsCnt: Int
   private var guessCnt = 0
   private val repeatClr: Boolean
   private val colors: String
   private var combo = ""
   private val guesses = mutableListOf<CharArray>()
   private val results = mutableListOf<CharArray>()
   constructor(codeLen: Int, colorsCnt: Int, guessCnt: Int, repeatClr: Boolean) {
       val color = "ABCDEFGHIJKLMNOPQRST"
       this.codeLen = codeLen.coerceIn(4, 10)
       var cl = colorsCnt
       if (!repeatClr && cl < this.codeLen) cl = this.codeLen
       this.colorsCnt = cl.coerceIn(2, 20)       
       this.guessCnt = guessCnt.coerceIn(7, 20)    
       this.repeatClr = repeatClr
       this.colors = color.take(this.colorsCnt)
   }
   fun play() {
       var win = false
       combo = getCombo()
       while (guessCnt != 0) {
           showBoard()
           if (checkInput(getInput())) {
               win = true
               break
           }
           guessCnt--
       }
       println("\n\n--------------------------------")
       if (win) {
           println("Very well done!\nYou found the code: $combo")
       }
       else {
           println("I am sorry, you couldn't make it!\nThe code was: $combo")
       }
       println("--------------------------------\n")
   }
   private fun showBoard() {
       for (x in 0 until guesses.size) {
           println("\n--------------------------------")
           print("${x + 1}: ")
           for (y in guesses[x]) print("$y ")
           print(" :  ")
           for (y in results[x]) print("$y ")
           val z = codeLen - results[x].size
           if (z > 0) print("- ".repeat(z))
       }
       println("\n")
   }
   private fun getInput(): String {
       while (true) {
           print("Enter your guess ($colors): ")
           val a = readLine()!!.toUpperCase().take(codeLen)          
           if (a.all { it in colors } ) return a
       }
   }
   private fun checkInput(a: String): Boolean {
       guesses.add(a.toCharArray())
       var black = 0
       var white = 0
       val gmatch = BooleanArray(codeLen)
       val cmatch = BooleanArray(codeLen)
       for (i in 0 until codeLen) {
           if (a[i] == combo[i]) {
               gmatch[i] = true
               cmatch[i] = true
               black++
           }
       }
       for (i in 0 until codeLen) {
           if (gmatch[i]) continue
           for (j in 0 until codeLen) {
               if (i == j || cmatch[j]) continue
               if (a[i] == combo[j]) {
                   cmatch[j] = true
                   white++
                   break
               }
           }
       }   
       val r = mutableListOf<Char>()
       r.addAll("X".repeat(black).toList())
       r.addAll("O".repeat(white).toList())    
       results.add(r.toCharArray())
       return black == codeLen
   }
   private fun getCombo(): String {
       val c =  StringBuilder()
       val clr = StringBuilder(colors)
       for (s in 0 until codeLen) {
           val z = rand.nextInt(clr.length)
           c.append(clr[z])
           if (!repeatClr) clr.deleteCharAt(z)
       }
       return c.toString()
   }

}

fun main(args: Array<String>) {

   val m = Mastermind(4, 8, 12, false)
   m.play()

}</lang>

Sample input/output (showing last 2 guesses only):

Enter your guess (ABCDEFGH): hcgb

--------------------------------
1: A B C D  :  O - - - 
--------------------------------
2: E F G H  :  X O O - 
--------------------------------
3: E G F A  :  O O - - 
--------------------------------
4: G F E A  :  O O - - 
--------------------------------
5: F H G B  :  X X O - 
--------------------------------
6: F B G C  :  X O - - 
--------------------------------
7: F G A B  :  X O - - 
--------------------------------
8: A H G C  :  X O - - 
--------------------------------
9: G H D B  :  X O O - 
--------------------------------
10: H A G B  :  X X X - 
--------------------------------
11: H C G B  :  X X X - 

Enter your guess (ABCDEFGH): hegb


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

Lua

Based on C++ <lang lua> 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() </lang>

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

Translation of: Perl 6

<lang perl>use List::Util qw(any);

print 'Enter pool size, puzzle size, attempts allowed: '; ($pool,$length,$tries) = split /\s+/, <>; $length = 4 if $length eq or $length < 3 or $length > 11; $pool = 6 if $pool eq or $pool < 2 or $pool > 21; $tries = 10 if $tries eq or $tries < 7 or $tries > 21;

@valid = sort { -1 + 2*int(rand 2) } ('A' .. 'T')[0..$pool-1]; @puzzle = @valid[0..$length-1];

$black = '●'; $white = '○';

while () {

   header();
   print "$_\n" for @guesses;
   lose() if  @guesses == $tries;
   @guess = get_guess();
   next unless is_valid(@guess);
   $score = score(\@puzzle, \@guess);
   win() if $score eq join ' ', ($black) x $length;
   push @guesses, join(' ', @guess) . ' :: ' . $score;

}

sub score {

   local *puzzle = shift;
   local *guess  = shift;
   my @score;
   for $i (0..$length-1) {
       if    (     $puzzle[$i] eq $guess[$i]) { push @score, $black }
       elsif (any {$puzzle[$i] eq $_} @guess) { push @score, $white }
       else                                   { push @score, '-'    }
   }
   join ' ', reverse sort @score;

}

sub header {

   $num = $tries - @guesses;
   print  "Valid letter, but wrong position: ○ - Correct letter and position: ●\n";
   print  "Guess the $length element sequence containing the letters " . join(', ', sort @valid) . "\n";
   printf "Repeats are not allowed. You have $num guess%s remaining\n", $num > 1 ? 'es' : ;

}

sub get_guess { print 'Your guess?: '; $g = <>; return split /\s*/, uc $g }

sub is_valid { $length == @_ }

sub win { print 'You win! The correct answer is: ' . join(' ',@puzzle) . "\n"; exit }

sub lose { print 'Too bad, you ran out of guesses. The solution was: ' . join(' ',@puzzle) . "\n"; exit } </lang> Sample output, omitting redundant instructions.

Output:
Valid letter, but wrong position: ○ - Correct letter and position: ●
Guess the 4 element sequence containing the letters A, B, C, D, E, F
Repeats are not allowed. You have 10 guesses remaining
Your guess?: a b c e
A B C E :: ○ ○ ○ -
Your guess?: b a d c
Repeats are not allowed. You have 8 guesses remaining
A B C E :: ○ ○ ○ -
B A D C :: ● ● ○ ○
Your guess?: a b c d
Repeats are not allowed. You have 7 guesses remaining
A B C E :: ○ ○ ○ -
B A D C :: ● ● ○ ○
A B C D :: ○ ○ ○ ○
Your guess?: b d a c
You win! The correct answer is: B D A C

Perl 6

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. <lang perl6>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/;
       Valid letter, but wrong position: ○ - Correct letter and position: ●
       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 }

}</lang>

Sample output:
Valid letter, but wrong position: ○ - Correct letter and position: ●
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

More checks could have been added   (for illegal inputs and illegal options). <lang rexx>/*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;  L@abc=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, L@abc), 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</lang>

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 !!  │
          │                                         │
          └─────────────────────────────────────────┘

Ring

<lang ring>

  1. Project : Mastermind

colors = ["A", "B", "C", "D"] places = list(2) mind = list(len(colors)) rands = list(len(colors)) master = list(len(colors)) test = list(len(colors)) guesses = 7 repeat = false nr = 0

if repeat

  for n = 1 to len(colors)
       while true
                 rnd = random(len(colors)-1) + 1
                 if rands[rnd] != 1
                    mind[n] = rnd
                    rands[rnd] = 1
                   exit
                 ok
       end     
  next

else

  for n = 1 to len(colors)
       rnd = random(len(colors)-1) + 1
       mind[n] = rnd
   next

ok

for n = 1 to len(colors)

     master[n] = char(64+mind[n])

next while true

        for p = 1 to len(places)
              places[p] = 0
        next
        nr = nr + 1
        see "Your guess (ABCD)? "
        give testbegin
        for d = 1 to len(test)
              test[d] = testbegin[d]
        next 
        flag = 1
        for n = 1 to len(test)
              if upper(test[n]) != master[n]
                 flag = 0
              ok
        next
        if flag = 1
           exit
        else
           for x = 1 to len(master)
                 if upper(test[x]) = master[x]
                     places[1] = places[1] + 1
                 ok                  
           next
           mastertemp = master
           for p = 1 to len(test)
                 pos = find(mastertemp, upper(test[p]))
                 if pos > 0 
                    del(mastertemp, pos)
                    places[2] = places[2] + 1
                 ok
           next 
        ok
        place1 = places[1] 
        place2 = places[2] - place1
        place3 = len(master) - (place1 + place2)
        showresult(test, place1, place2, place3)
        if nr = guesses
           exit
        ok

end see "Well done!" + nl see "End of game" + nl

func showresult(test, place1, place2, place3)

       see "" + nr + " : " 
       for r = 1 to len(test)
            see test[r]
       next
       see " : "
       for n1 = 1 to place1
             see "X" + " "
       next
       for n2 = 1 to place2
             see "O" + " "
       next
       for n3 = 1 to place3
             see "-" + " "
       next
       see nl

</lang> Output:

Your guess (ABCD)? BCDA
1 : BCDA : X X O - 
Your guess (ABCD)? BCDB
2 : BCDB : X X X - 
Your guess (ABCD)? BCBB
3 : BCBB : X X X - 
Your guess (ABCD)? BCAB
Well done!
End of game

zkl

Translation of: C++

<lang zkl>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();</lang>

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