Bulls and Cows


Task

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
   print the score for the guess


The score is computed as:

 The player wins if the guess is the same as the randomly chosen number, and the program ends.
 A score of one bull is accumulated for each digit in the guess that equals the corresponding digit in the randomly chosen initial number.
 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.


Related tasks

   Bulls and cows/Player
   Guess the number
   Guess the number/With Feedback
   Mastermind


#!/usr/bin/env golosh
----
This module is the Bulls and Cows game.
----
module Bullsandcows
 
import gololang.Decorators
import gololang.Functions
import gololang.IO
import java.util
 
function main = |args| {
  while true {
    let secret = create4RandomNumbers()
    println("Welcome to Bulls And Cows")
    while true {
      println("Please enter a 4 digit number")
      println("(with only the digits 1 to 9 and no repeated digits, for example 2537)")
      let guess = readln("guess: ")
      let result = validateGuess(guess)
      if result: isValid() {
        let bulls, cows = bullsAndOrCows(result: digits(), secret)
        if bulls is 4 {
          println("You win!")
          break
        }
        println("bulls: " + bulls)
        println("cows:  " + cows)
      } else {
        println(result: message())
      }
    }
  }
}
 
function create4RandomNumbers = {
  let numbers = vector[1, 2, 3, 4, 5, 6, 7, 8, 9]
  Collections.shuffle(numbers)
  let shuffled = numbers: subList(0, 4)
  return shuffled
}
 
union Result = {
  Valid = { digits }
  Invalid = { message }
}
 
@checkArguments(isString())
function validateGuess = |guess| {
  var digits = []
  try {
    let number = guess: toInt()
    digits = number: digits()
    if digits: exists(|d| -> d is 0) {
      return Result.Invalid("No zeroes please")
    }
    if digits: size() isnt 4  {
      return Result.Invalid("Four digits please")
    }
    let digitSet = set[ digit foreach digit in digits ]
    if digitSet: size() < digits: size() {
      return Result.Invalid("No duplicate numbers please")
    }
  } catch(e) {
    return Result.Invalid("Numbers only please")
  }
  return Result.Valid(digits)
}
 
let is4Vector = |arg| -> arg oftype java.util.List.class and arg: size() is 4
 
@checkArguments(is4Vector, is4Vector)
function bullsAndOrCows = |guess, secret| {
  var bulls = 0
  var cows = 0
  foreach i in [0..4] {
    let guessNum = guess: get(i)
    let secretNum = secret: get(i)
    if guessNum is secretNum {
      bulls = bulls + 1
    } else if secret: exists(|num| -> guessNum is num) {
      cows = cows + 1
    }
  }
  return [bulls, cows]
}
 
augment java.lang.Integer {
 
  function digits = |this| {
    var remaining = this
    let digits = vector[]
    while remaining > 0 {
      digits: prepend(remaining % 10)
      remaining = remaining / 10
    }
    return digits
  }
}
 