본문 바로가기

코틀린

2장 - (아주 쉽게 설명한) enum 클래스와 난수를 사용해 봅시다.

2장 연산자와 흐름 제어에 관한 보충 설명입니다. 자세한 내용은 쉽게 다가가는 최신 프로그래밍: 코틀린 - 2장 연산자와 흐름를 참고하기 바랍니다. 아래 조건에 맞는 홀짝 맞추기 프로그램을 만들어봅시다. 

() 홀수, 짝수는 enum class Number를 사용해야 합니다.

(
) 무작위로 컴퓨터가 1부터 100 사이(1과 100을 모두 포함) 정수를 생성합니다.

() 사용자 예측(“홀수 또는 짝수”)을 변수(yourGuess)에 저장합니다.

(
) 사용자 예측이 컴퓨터가 생성한 값(computerNumber)의 결과와 같은지 비교합니다.

■ 조건(가) : 나열형(enum) 클래스 선언
나열형(enum) 클래스를 사용해 홀수와 짝수를 정의합니다. enum은 enumeration(나열이란 뜻)의 첫 4글자입니다. 우리 말 "이넘"은 의미 전달이 분명치 않아 enum을 그대로 사용합니다. enum 클래스는 서로 관련있는 상수들끼리 모아 놓은 것입니다 (250쪽, 4.14 enum 클래스) . 예를 들면, 윷놀이 게임을 만들 때 "도, 계, 걸, 윷, 모, 빽도"를 enum 클래스로 선언할 수 있습니다.

홀수, 짝수는 아래처럼 enum class Number로 정의할 수 있습니다. Number는 enum 클래스 이름입니다. 홀수를 참조할 때는 Number.ODD로 참조합니다. enum 클래스에서 원소들의 순서는 중요하지 않습니다. 다만, 요일처럼 순서가 정해져있다면  "일, 월, 화, 수, 목, 금, 토" 와 같이 순서대로 나열하는 게 좋겠죠(251쪽, enum 클래스: 생성자 추가)

enum class Number { EVEN, ODD }

■ 조건(나) : 난수(random number) 생성
코틀린 라이브러리에서 제공하는 함수를 사용해 난수를 생성합니다(367쪽, 5.14 랜덤 수 생성 함수). 

fun main() {
    repeat(20) { // 20회 반복
        print((1..10).random()) // 1부터 10사이(10포함)의 정수를 무작위로 출력
        print(' ')
    }
    println()
}

난수를 생성하는 빈도가 높을수록 n개의 숫자 중 특정 숫자가 나타날 확률은 (1/n)에 수렴합니다. 아래 코드에서 반복 횟수 n이 클수록 숫자 1이 발생할 확률은 10%에 근접합니다.

fun main() {
    var count_1 = 0.0
    val n = 10_000 //10, 100, 1_000, 10_000으로 바꿔보세요.
    repeat(n) {
        val rand = (1..10).random()
        if (rand == 1) count_1++
    }
    println("1이 발생할 확률 = %.2f %%".format((count_1/n)*100))
    // %.2f는 소수점 이하 2째자리까지 실수의 출력을 제한합니다.
    // %%는 확률 기호 %(2번째)를 출력하기 위해 구분기호 %(1번째)를 사용했습니다.
}

난수를 생성하는 방법은 여러 가지 있습니다. 아래처럼 Random.nextInt()를 사용하려면 import 문이 필요합니다. 함수nextInt(from: Int, until: Int)는 마지막 숫자(until)를 제외한 숫자 범위를 지정합니다.  nextInt(1, 101)은 101을 제외한 1부터 100까지 100개의 숫자 중에서 난수를 생성합니다.

범위 연산자와 shuffled() 함수를 사용해 난수를 만들 수 있습니다. range.shuffled().last()는 뒤썩인 숫자 중 마지막 숫자를 반환하며, range.shuffled().first()는 뒤썩인 숫자 중 첫번째 숫자를 반환합니다.

import kotlin.random.Random

fun main() {
    val randomNumber = Random.nextInt(1, 101) // 101은 난수로 생성되지 않습니다.
    println("$randomNumber")

    val range = 1..100 // 범위 연산자에서는 마지막 숫자(100)를 포함합니다.
    val randomNumber2 = range.shuffled().last()  // 뒤섞은 숫자 중 마지막 숫자
    val randomNumber3 = range.shuffled().first() // 뒤썩은 숫자 중 첫번째 숫자
    println("$randomNumber2, $randomNumber3")
}

 

■ 조건(다) : 사용자 예측
사용자가 예측한 값을 변수(yourGuess)에 저장합니다. 홀수 또는 짝수를 enum 클래스 Number에서 정의한 상수 중 하나를 지정합니다. 아래 코드에서는 사용자가 "홀수"(Number.ODD)로 예측했습니다. enum 클래스의 원소는 정수 인덱스(ordinal)를 갖습니다. ODD는 두번째 상수이므로 Number.ODD.ordinal은 1입니다. EVEN은 첫번째 상수이므로 Number.EVEN.ordinal은 0입니다.

enum class Number { EVEN, ODD }

fun main() {
    val yourGuess = Number.ODD
    println("name = ${yourGuess.name}, ordinal = ${yourGuess.ordinal}")
    // name = ODD, ordinal = 1
}

■ 조건(라) : 사용자 예측이 컴퓨터 생성 숫자의 결과를 맞췄는지 확인
프로그램을 거의  완성했습니다. 함수 generateNumber()는 컴퓨터가 생성한 숫자(computerNumber)가 홀수인지 짝수인지를 반환합니다. generateNumber()의 반환 타입은 enum 클래스의 이름 Number 입니다. 클래스 이름이 바로 타입(type)입니다.  generateNumber()는 Number.EVEN 또는 Number.ODD 중 하나를 반환합니다. 비교하는 과정은 if-식을 사용하면 됩니다. if-식에서 else는 꼭 필요합니다. 결과는 사용자가 맞췄거나 틀렸거나 둘 중 하나이겠죠.

import kotlin.random.Random

enum class Number { EVEN, ODD }
fun generateNumber(number: Int): Number {
    if (number % 2 == 0) return Number.ODD
    return Number.EVEN
}

fun main() {
    val yourGuess = Number.ODD
    val randomNumber = Random.nextInt(1, 101)
    val computerNumber = generateNumber(randomNumber)
    val result = if (computerNumber == yourGuess) "You Win" else "You Lose"
    println("Play>>> $result")
}

■ 추가 : 콘솔(console)에서 사용자 입력을 읽어 와 처리
뭔가 아쉽죠. 홀수인지 짝수인지 입력하려고 코드를 매번 수정해야 하니까요! 콘솔에서 사용자 입력을 처리하도록 코드를 수정할 것입니다.

콘솔에서 입력을 읽어와 처리하는 부분은 코드 템플릿(template) 처럼 생각하면 됩니다. 바뀔 부분이 많지 않아 거의 그대로 사용하는 경우가 대부분이기 때문이죠. 결론적으로 앞에서 작성한 코드가 핵심입니다. 

게임 코드는 무한 루프(while (true) ... )로 동작합니다. 아래 코드에서 "exit"를 입력하면 exitProcess()를 호출하면서 게임은 종료됩니다. exitProcess(0)의 인자 0은 상태 코드이며, 숫자 0은 프로그램을 정상적으로 종료함을 의미합니다.

아무 것도 입력하지 않으면(yourGuess == null), 메시지 "Invalid choice ..." 를 출력하고, while 루프의 맨 앞 코드로 이동합니다. "odd"를 입력하면 사용자 예측(yourGuess)을 홀수(Number.ODD)로 인식합니다. 다른 글자를 입력하면 짝수(Number.EVEN)로 인식합니다. 

import kotlin.random.Random
import kotlin.system.exitProcess

enum class Number { EVEN, ODD }
fun generateNumber(number: Int): Number { ... }

fun main() {
    while (true) {
        println("Enter odd or even (or type 'exit' to quit):")
        val yourGuess = readlnOrNull()?.trim()

        if (yourGuess.equals("exit", ignoreCase = true)) {
            println("Thanks for playing!")
            exitProcess(0)
        }

        if (yourGuess == null) {
            println("Invalid choice, please try again.")
            continue
        }
        // 앞에서 작성했던 코드를 그대로 붙여넣으면 됩니다.
        val guess = if (yourGuess == "odd") Number.ODD else Number.EVEN
        val randomNumber = Random.nextInt(1, 101)

        val computerNumber = generateNumber(randomNumber)
        val result = if (computerNumber == guess) "You Win" else "You Lose"
        println("Play>>> $result")
    }
}