Bulls and cows: Difference between revisions

From Rosetta Code
Content added Content deleted
(BASIC added)
m (→‎{{header|ALGOL 68}}: works with...)
Line 14: Line 14:


{{works with|ALGOL 68|Standard - no extensions to language used}}
{{works with|ALGOL 68|Standard - no extensions to language used}}

{{works with|ALGOL 68G|Any - tested with release mk15-0.8b.fc9.i386}}
{{works with|ALGOL 68G|Any - tested with release mk15-0.8b.fc9.i386}}

{{works with|ELLA ALGOL 68|Any (with appropriate job cards) - tested with release 1.8.8d.fc9.i386}}
{{works with|ELLA ALGOL 68|Any (with appropriate job cards) - tested with release 1.8.8d.fc9.i386}}
<lang algol>STRING digits = "123456789";
<lang algol>STRING digits = "123456789";

Revision as of 10:36, 20 May 2009

Task
Bulls and cows
You are encouraged to solve this task according to the task description, using any language you may know.

This is an old game played with pencil and paper that was later implemented on computer.

The task is for the program to create a four digit random number from the digits 1 to 9, without duplication. The program should ask for guesses to this number, reject guesses that are malformed, then print the score for the guess.

The score is computed as:

  1. The player wins if the guess is the same as the randomly chosen number, and the program ends.
  2. A score of one bull is accumulated for each digit in the guess that equals the corresponding digit in the randomly chosen initial number.
  3. A score of one cow is accumulated for each digit in the guess that also appears in the randomly chosen number, but in the wrong position.

ALGOL 68

Translation of: Python
Works with: ALGOL 68 version Standard - no extensions to language used
Works with: ALGOL 68G version Any - tested with release mk15-0.8b.fc9.i386
Works with: ELLA ALGOL 68 version Any (with appropriate job cards) - tested with release 1.8.8d.fc9.i386

<lang algol>STRING digits = "123456789";

[4]CHAR chosen; STRING available := digits; FOR i TO UPB chosen DO

   INT c = ENTIER(random*UPB available)+1; 
   chosen[i] := available[c]; 
   available := available[:c-1]+available[c+1:]

OD;

COMMENT print((chosen, new line)); # Debug # END COMMENT

OP D = (INT d)STRING: whole(d,0); # for formatting an integer #

print (("I have chosen a number from ",D UPB chosen," unique digits from 1 to 9 arranged in a random order.", new line, "You need to input a ",D UPB chosen," digit, unique digit number as a guess at what I have chosen", new line));

PRIO WITHIN = 5, NOTWITHIN = 5; OP WITHIN = (CHAR c, []CHAR s)BOOL: char in string(c,LOC INT,s); OP NOTWITHIN = (CHAR c, []CHAR s)BOOL: NOT ( c WITHIN s );

INT guesses := 0, bulls, cows; WHILE

   STRING guess;
   guesses +:= 1;
   WHILE 
       # get a good guess #
       print((new line,"Next guess [",D guesses,"]: "));
       read((guess, new line));
       IF UPB guess NE UPB chosen THEN
           FALSE
       ELSE
           BOOL ok;
           FOR i TO UPB guess WHILE
               ok := guess[i] WITHIN digits AND guess[i] NOTWITHIN guess[i+1:]
           DO SKIP OD;
           NOT ok 
       FI
   DO
       print(("Problem, try again. You need to enter ",D UPB chosen," unique digits from 1 to 9", new line))
   OD;
  1. WHILE #
   guess NE chosen

DO

   bulls := cows := 0;
   FOR i TO UPB chosen DO
       IF guess[i] = chosen[i] THEN
           bulls +:= 1
       ELIF guess[i] WITHIN chosen THEN
           cows +:= 1
       FI
   OD;
   print(("  ",D bulls," Bulls",new line,"  ",D cows," Cows"));

OD; print((new line, "Congratulations you guessed correctly in ",D guesses," attempts.",new line))</lang>

BASIC

Works with: FreeBASIC

<lang freebasic> DIM secret AS String DIM guess AS String DIM c AS String DIM AS Integer bulls, cows, guesses, i

RANDOMIZE DO WHILE LEN(secret) < 4

   c = CHR$(INT(RND*10) + 48)
   IF INSTR(secret, c) = 0 THEN secret = secret+c

LOOP

guesses = 0 DO

   INPUT "Guess a 4-digit number with no duplicate digits: "; guess
   IF LEN(guess) = 0 THEN EXIT DO
   IF LEN(guess) <> 4 OR VAL(guess) = 0 THEN
       PRINT "** You should enter 4 numeric digits!"

CONTINUE DO

   END IF
   bulls=0: cows=0: guesses = guesses+1
   FOR i = 1 TO 4
       c = MID$(secret, i, 1)

IF MID$(guess, i, 1) = c THEN bulls = bulls+1 ELSEIF INSTR(guess, c) THEN cows = cows+1 END IF

   NEXT i
   PRINT bulls; " bulls, "; cows; " cows"
   IF guess = secret THEN
       PRINT "You won after "; guesses; " guesses!"

EXIT DO

   END IF

LOOP </lang>

C

Library: ncurses

<lang c>#include <stdio.h>

  1. include <stdarg.h>
  2. include <stdlib.h>
  3. include <stdbool.h>
  4. include <curses.h>
  5. include <string.h>
  1. define MAX_NUM_TRIES 72
  2. define LINE_BEGIN 7
  3. define LAST_LINE 18

int yp=LINE_BEGIN, xp=0;

char number[5]; char guess[5];

  1. define MAX_STR 256

void mvaddstrf(int y, int x, char *fmt, ...) {

 va_list args;
 char buf[MAX_STR];
 
 va_start(args, fmt);
 vsprintf(buf, fmt, args);
 move(y, x);
 clrtoeol();
 addstr(buf);
 va_end(args);

}

void ask_for_a_number() {

 int i=0;
 char symbols[] = "123456789";
 move(5,0); clrtoeol();
 addstr("Enter four digits: ");
 while(i<4) {
   int c = getch();
   if ( (c >= '1') && (c <= '9') && (symbols[c-'1']!=0) ) {
     addch(c);
     symbols[c-'1'] = 0;
     guess[i++] = c;
   }
 }

}

void choose_the_number() {

 int i=0, j;
 char symbols[] = "123456789";
 while(i<4) {
   j = rand() % 9;
   if ( symbols[j] != 0 ) {
     number[i++] = symbols[j];
     symbols[j] = 0;
   }
 }

}</lang>

The following function contains the code to check how many bulls and cows there are.

<lang c>bool take_it_or_not() {

 int i;
 int cows=0, bulls=0;
 for(i=0; i < 4; i++) {
   if ( number[i] == guess[i] ) {
     bulls++;
   } else if ( strchr(number, guess[i]) != NULL ) {
     cows++;
   }
 }
 move(yp, xp);
 addstr(guess); addch(' ');
 if ( bulls == 4 ) { yp++; return true; }
 if ( (cows==0) && (bulls==0) ) addch('-');
 while( cows-- > 0 ) addstr("O");
 while( bulls-- > 0 ) addstr("X");
 yp++;
 if ( yp > LAST_LINE ) {
   yp = LINE_BEGIN;
   xp += 10;
 }
 return false;

}

bool ask_play_again() {

 int i;
 while(yp-- >= LINE_BEGIN) {
   move(yp, 0); clrtoeol();
 }
 yp = LINE_BEGIN; xp = 0;
 move(21,0); clrtoeol();
 addstr("Do you want to play again? [y/n]");
 while(true) {
   int a = getch();
   switch(a) {
   case 'y':
   case 'Y':
     return true;
   case 'n':
   case 'N':
     return false;
   }
 }

}


int main() {

 bool bingo, again;
 int tries = 0;
 initscr(); cbreak(); noecho();
 clear();
 number[4] = guess[4] = 0;
 mvaddstr(0,0, "I choose a number made of 4 digits (from 1 to 9) without repetitions\n"
               "You enter a number of 4 digits, and I say you how many of them are\n"
               "in my secret number but in wrong position (cows or O), and how many\n"
               "are in the right position (bulls or X)");
 do {
   move(20,0); clrtoeol(); move(21, 0); clrtoeol();
   srand(time(NULL));
   choose_the_number();
   do {
     ask_for_a_number();
     bingo = take_it_or_not();
     tries++;
   } while(!bingo && (tries < MAX_NUM_TRIES));
   if ( bingo ) 
     mvaddstrf(20, 0, "You guessed %s correctly in %d attempts!", number, tries);
   else
     mvaddstrf(20,0, "Sorry, you had only %d tries...; the number was %s", 

MAX_NUM_TRIES, number);

   again = ask_play_again();
   tries = 0; 
 } while(again);
 nocbreak(); echo(); endwin();
 return EXIT_SUCCESS;

}</lang>

C++

<lang cpp>

  1. include <iostream>
  2. include <string>
  3. include <algorithm>
  4. include <cstdlib>

bool contains_duplicates(std::string s) {

 std::sort(s.begin(), s.end());
 return std::adjacent_find(s.begin(), s.end()) != s.end();

}

void game() {

 typedef std::string::size_type index;
 std::string symbols = "0123456789";
 int const selection_length = 4;
 std::random_shuffle(symbols.begin(), symbols.end());
 std::string selection = symbols.substr(0, selection_length);
 std::string guess;
 while (std::cout << "Your guess? ", std::getline(std::cin, guess))
 {
   if (guess.length() != selection_length
       || guess.find_first_not_of(symbols) != std::string::npos
       || contains_duplicates(s))
   {
     std::cout << guess << " is not a valid guess!";
     continue;
   }
   int bulls = 0;
   int cows = 0;
   for (index i = 0; i != selection_length; ++i)
   {
     index pos = selection.find(guess[i]);
     if (pos == i)
       ++bulls;
     else if (pos != std::string::npos)
       ++cows;
   }
   std::cout << bulls << " bulls, " << cows << " cows.\n";
   if (bulls == selection_length)
   {
     std::cout << "Congratulations! You have won!\n";
     return:
   }
 }
 std::cerr << "Oops! Something went wrong with input, or you've entered end-of-file!\nExiting ...\n";
 std::exit(EXIT_FAILURE);

}

int main() {

 std::cout << "Welcome to bulls and cows!\nDo you want to play? ";
 std::string answer;
 while (true)
 {
   while (true)
   {
     if (!std::getline(std::cin, answer))
     {
       std::cout << "I can't get an answer. Exiting.\n";
       return EXIT_FAILURE;
     }
     if (answer == "yes" || answer == "Yes" || answer == "y" || answer == "Y")
       break;
     if (answer == "no" || answer == "No" || answer == "n" || answer == "N")
     {
       std::cout << "Ok. Goodbye.\n";
       return EXIT_SUCCESS;
     }
     std::cout << "Please answer yes or no: ";
   }
   game(); 
   std::cout << "Another game? ";
 }

} </lang>

Fan

<lang Fan>

    • Bulls and cows. A game pre-dating, and similar to, Mastermind.

class BullsAndCows {

 Void main()
 {
   digits := [1,2,3,4,5,6,7,8,9]
   size := 4
   chosen := [,]
   size.times { chosen.add(digits.removeAt(Int.random(0..<digits.size))) }
   echo("I've chosen $size unique digits from 1 to 9 at random.
         Try to guess my number!")
   guesses := 0
   while (true) // game loop
   {
     guesses += 1
     guess := Int[,]
     while (true) // input loop
     {
       // get a good guess
       Sys.out.print("\nNext guess [$guesses]: ")
       Sys.out.flush
       inString := Sys.in.readLine?.trim ?: ""
       inString.each |ch|
       { if (ch >= '1' && ch <= '9' && !guess.contains(ch)) guess.add(ch-'0') }
       if (guess.size == 4)
         break // input loop
       echo("Oops, try again. You need to enter $size unique digits from 1 to 9")
     }
     if (guess.all |v, i->Bool| { return v == chosen[i] })
     {
       echo("\nCongratulations! You guessed correctly in $guesses guesses")
       break // game loop
     }
     bulls := 0
     cows  := 0
     (0 ..< size).each |i|
     {
       if (guess[i] == chosen[i])
         bulls += 1
       else if (chosen.contains(guess[i]))
         cows += 1
     }
     echo("\n  $bulls Bulls\n  $cows Cows")
   }
 }

} </lang>

Forth

Works with: GNU Forth

<lang forth> include random.fs

create hidden 4 allot

ok? ( str -- ? )
 dup 4 <> if 2drop false exit then
 1 9 lshift 1- -rot
 bounds do
   i c@ '1 -
   dup 0 9 within 0= if 2drop false leave then
   1 swap lshift over and
   dup 0= if nip leave then
   xor
 loop 0<> ;
init
 begin
   hidden 4 bounds do 9 random '1 + i c! loop
   hidden 4 ok?
 until ;
check? ( addr -- solved? )
 0
 4 0 do
   over i + c@
   4 0 do
     dup hidden i + c@ = if     swap
       i j = if 8 else 1 then + swap
     then
   loop drop
 loop nip
 8 /mod tuck . ." bulls, " . ." cows"
 4 = ;
guess: ( "1234" -- )
 bl parse 2dup ok? 0= if 2drop ." Bad guess! (4 unique digits, 1-9)" exit then
 drop check? if cr ." You guessed it!" then ;

</lang>

init  ok
guess: 1234 1 bulls, 0 cows ok
guess: 1567 1 bulls, 1 cows ok
guess: 1895 2 bulls, 1 cows ok
guess: 1879 4 bulls, 0 cows
You guessed it! ok

Java

<lang java5>import java.util.InputMismatchException; import java.util.Random; import java.util.Scanner;

public class BullsAndCows{ public static void main(String[] args){ Random gen= new Random(); int target= 0; while(hasDupes(target= (gen.nextInt(9000) + 1000))); String targetStr = target +""; boolean guessed = false; Scanner input = new Scanner(System.in); int guesses = 0; do{ int bulls = 0; int cows = 0; System.out.print( "Guess a 4-digit number with no duplicate digits: "); int guess; try{ guess = input.nextInt(); if(hasDupes(guess)) continue; }catch(InputMismatchException e){ continue; } guesses++; String guessStr = guess + ""; for(int i= 0;i < 4;i++){ if(guessStr.charAt(i) == targetStr.charAt(i)){ bulls++; }else if(targetStr.contains(guessStr.charAt(i)+"")){ cows++; } } if(bulls == 4){ guessed = true; }else{ System.out.println(cows+" Cows and "+bulls+" Bulls."); } }while(!guessed); System.out.println("You won after "+guesses+" guesses!"); }

public static boolean hasDupes(int num){ boolean[] digs = new boolean[10]; while(num > 0){ if(digs[num%10]) return true; digs[num%10] = true; num/= 10; } return false; } }</lang> Output:

Guess a 4-digit number with no duplicate digits: 5834
2 Cows and 0 Bulls.
Guess a 4-digit number with no duplicate digits: 1234
1 Cows and 0 Bulls.
Guess a 4-digit number with no duplicate digits: 4321
1 Cows and 0 Bulls.
Guess a 4-digit number with no duplicate digits: 3421
0 Cows and 1 Bulls.
Guess a 4-digit number with no duplicate digits: 8412
0 Cows and 0 Bulls.
Guess a 4-digit number with no duplicate digits: 3560
1 Cows and 1 Bulls.
Guess a 4-digit number with no duplicate digits: 3650
0 Cows and 2 Bulls.
Guess a 4-digit number with no duplicate digits: 3759
2 Cows and 2 Bulls.
Guess a 4-digit number with no duplicate digits: 3975
2 Cows and 2 Bulls.
Guess a 4-digit number with no duplicate digits: 3957
You won after 10 guesses!

Perl

<lang perl>use strict; use Data::Random qw(rand_set);

my $size = 4; my $chosen = join("", rand_set( set => ["1".."9"], size => $size));

print "I've chosen a number from $size unique digits from 1 to 9; you need to input $size unique digits to guess my number\n";

my $guesses = 0; my $guess; while(1) {

   $guesses++;
   while(1) {
     print "\nNext guess [$guesses]: ";
     $guess = <STDIN>;
     chomp $guess;
     if ( !checkguess($guess) ) {

print "$size digits, no repetition, no 0... retry\n";

     } else { last; }
   }
   if ( $guess eq $chosen ) {

print "You did it in $guesses attempts!\n"; last;

   } else {

my $bulls=0; my $cows=0; for my $i (0 .. $size-1) { if ( substr($guess, $i, 1) eq substr($chosen, $i, 1) ) { $bulls++; } elsif ( index($chosen, substr($guess, $i, 1)) >= 0 ) { $cows++; } } print "$cows cows, $bulls bulls\n";

   }

}

sub checkguess($) {

   my $g = shift;
   my %h = ();
   for my $k ( split(//, $g) ) {

return 0 if exists $h{$k}; $h{$k} = 1;

   }
   return ( $g =~ /^[1-9]{4}$/ );

}</lang>

Python

<lang python>

Bulls and cows. A game pre-dating, and similar to, Mastermind.

import random

digits = '123456789' size = 4 chosen = .join(random.sample(digits,size))

  1. print chosen # Debug

print I have chosen a number from %s unique digits from 1 to 9 arranged in a random order. You need to input a %i digit, unique digit number as a guess at what I have chosen % (size, size) guesses = 0 while True:

   guesses += 1
   while True:
       # get a good guess
       guess = raw_input('\nNext guess [%i]: ' % guesses).strip()
       if len(guess) == size and \
          all(char in digits for char in guess) \
          and len(set(guess)) == size:
           break
       print "Problem, try again. You need to enter %i unique digits from 1 to 9" % size
   if guess == chosen:
       print '\nCongratulations you guessed correctly in',guesses,'attempts'
       break
   bulls = cows = 0
   for i in range(size):
       if guess[i] == chosen[i]:
           bulls += 1
       elif guess[i] in chosen:
           cows += 1
   print '  %i Bulls\n  %i Cows' % (bulls, cows)</lang>

Sample output:

I have chosen a number from 4 unique digits from 1 to 9 arranged in a random order.
You need to input a 4 digit, unique digit number as a guess at what I have chosen

Next guess [1]: 79
Problem, try again. You need to enter 4 unique digits from 1 to 9

Next guess [1]: 7983
  2 Bulls
  2 Cows

Next guess [2]: 7938

Congratulations you guessed correctly in 2 attempts

Smalltalk

Works with: GNU Smalltalk

<lang smalltalk>Object subclass: BullsCows [

 |number|
 BullsCows class >> new: secretNum [ |i|
   i := self basicNew.
   (self isValid: secretNum)
      ifFalse: [ SystemExceptions.InvalidArgument
                   signalOn: secretNum
                   reason: 'You need 4 unique digits from 1 to 9' ].
   i setNumber: secretNum.
   ^ i
 ]
 BullsCows class >> new [ |b| b := Set new.
    [ b size < 4 ]
      whileTrue: [ b add: ((Random between: 1 and: 9) displayString first) ].
    ^ self new: (b asString)
 ]
 BullsCows class >> isValid: num [
   ^ (num asSet size = 4) & ((num asSet includes: $0) not)
 ]
 setNumber: num [ number := num ]
 check: guess [ |bc| bc := Bag new.
    1 to: 4 do: [ :i |
      (number at: i) = (guess at: i)
        ifTrue: [ bc add: 'bulls' ]
        ifFalse: [
            (number includes: (guess at: i))
              ifTrue: [ bc add: 'cows' ]
        ]
    ].
    ^ bc
 ]

].

'Guess the 4-digits number (digits from 1 to 9, no repetition)' displayNl.

|guessMe d r tries| [

 tries := 0.
 guessMe := BullsCows new.
 [
   [
     'Write 4 digits: ' display.
     d := stdin nextLine.
     (BullsCows isValid: d)
   ] whileFalse: [
        'Insert 4 digits, no repetition, exclude the digit 0' displayNl
   ].
   r := guessMe check: d.
   tries := tries + 1.
   (r occurrencesOf: 'bulls') = 4
 ] whileFalse: [
   ('%1 cows, %2 bulls' % { r occurrencesOf: 'cows'. r occurrencesOf: 'bulls' })
     displayNl.
 ].
 ('Good, you guessed it in %1 tries!' % { tries }) displayNl.
 'Do you want to play again? [y/n]' display.
 ( (stdin nextLine) = 'y' )

] whileTrue: [ Character nl displayNl ].</lang>

Tcl

<lang tcl>proc main {} {

   fconfigure stdout -buffering none
   set length 4
   
   puts "I have chosen a number from $length unique digits from 1 to 9 arranged in a random order.

You need to input a $length digit, unique digit number as a guess at what I have chosen

   "
   
   while true {
       set word [generateWord $length]
       set count 1
       while {[set guess [getGuess $length]] ne $word} {
           printScore $length $word $guess
           incr count
       }
       puts "You guessed correctly in $count tries."
       if {[yn "Play again?"] eq "n"} break
   }

}

proc generateWord {length} {

   set chars 123456789
   for {set i 1} {$i <= $length} {incr i} {
       set idx [expr {int(rand() * [string length $chars])}]
       append word [string index $chars $idx]
       set chars [string replace $chars $idx $idx]
   }
   return $word
   # here's another way to generate word with no duplications
   set word ""
   while {[string length $word] < $length} {
       set char [expr {int(1 + 9*rand())}]
       if {[string first $char $word] == -1} {
           append word $char
       }
   }

}

proc getGuess {length} {

   puts -nonewline "Enter your guess: "
   while true {
       gets stdin guess
       if {[string match [string repeat {[1-9]} $length] $guess]} {
           return $guess
       }
       if {[string tolower [string trim $guess]] eq "quit"} {
           puts Bye
           exit
       }
       puts "The word must be $length digits between 1 and 9 inclusive.  Try again."
   }

}

proc printScore {length word guess} {

   set bulls 0
   set cows 0
   for {set i 0} {$i < $length} {incr i} {
       if {[string index $word $i] eq [string index $guess $i]} {
           incr bulls
           set word [string replace $word $i $i +]
       }
   }
   puts "  $bulls bulls"
   for {set i 0} {$i < $length} {incr i} {
       if {[set j [string first [string index $guess $i] $word]] != -1} {
           incr cows
           set word [string replace $word $j $j -]
       }
   }
   puts "  $cows cows"

}

proc yn {msg} {

   while true {
       puts -nonewline "$msg \[y/n] "
       gets stdin ans
       set char [string tolower [string index [string trim $ans] 0]]
       if {$char eq "y" || $char eq "n"} {
           return $char
       }
   }

}

main</lang>

Vedit macro language

<lang vedit> Buf_Switch(Buf_Free)

  1. 90 = Time_Tick // seed for random number generator
  2. 91 = 10 // random numbers in range 0 to 9

while (EOB_pos < 4) { // 4 digits needed

   Call("RANDOM")
   BOF Ins_Char(Return_Value + '0')
   Replace("(.)(.*)\1", "\1\2", REGEXP+BEGIN+NOERR)  // remove any duplicate

}

  1. 3 = 0

repeat (99) {

   Get_Input(10, "Guess a 4-digit number with no duplicate digits: ", NOCR)
   if (Reg_Size(10) == 0) { Break }                // empty string = exit
   Num_Eval_Reg(10)                                // check for numeric digits
   if (Chars_Matched != 4) {
       M("You should enter 4 numeric digits\n")
       Continue
   }
   Goto_Pos(4)                                     // count bulls
   Reg_Ins(10, OVERWRITE)
   #1 = Search("(.)...\1", REGEXP+BEGIN+ALL+NOERR)
   RS(10, "[", INSERT)                             // count cows
   RS(10, "]", APPEND)
   #2 = Search_Block(@10, 0, 4, REGEXP+BEGIN+ALL+NOERR) - #1
   #3++
   NT(#1, NOCR) M(" bulls,") NT(#2, NOCR) M(" cows\n") 
   if (#1 == 4) {
       M("You won after") NT(#3, NOCR) M(" guesses!\n")
       Break
   }

} Buf_Quit(OK) Return

//-------------------------------------------------------------- // Generate random numbers in range 0 <= Return_Value < #91 // #90 = Seed (0 to 0x7fffffff) // #91 = Scaling (0 to 0x10000)

RANDOM:
  1. 92 = 0x7fffffff / 48271
  2. 93 = 0x7fffffff % 48271
  3. 90 = (48271 * (#90 % #92) - #93 * (#90 / #92)) & 0x7fffffff

Return ((#90 & 0xffff) * #91 / 0x10000) </lang>