오늘부터는 본격적으로 게임을 만들어 볼 건데 먼저 랜덤 문제 하나와 답을 만들어 반환하는 메서드를 작성해 보자.
문제는 우선 구구단으로 하기로 하고 아래처럼 코드를 작성해서 테스트해 보자.
?> def make_quiz
?> n1 = rand(2..9)
?> n2 = rand(1..9)
?> ["#{n1} x #{n2} = ", n1 * n2]
>> end
=> :make_quiz
>> make_quiz
=> ["5 x 6 = ", 30]
>> make_quiz
=> ["3 x 3 = ", 9]
>> question, answer = make_quiz
=> ["6 x 8 = ", 48]
>> question
=> "6 x 8 = "
>> answer
=> 48
>>
make_quiz 메서드는 문자열로 표현한 구구단 문제 하나와 그에 대한 정답을 배열에 담아 반환한다.
구구단 문제를 랜덤하게 만들기 위해 'rand' 메서드를 사용했는데 Range 객체를 인수로 사용하여 원하는 범위의 정수를 쉽게 생성할 수 있었다.
make_quiz 메서드를 호출할 때 반환되는 배열 내 두 개의 요소 각각을 question 와 answer 두 개의 변수에 나눠 담을 수 있는 것이 보일 것이다.
이렇게 배열의 요소들을 여러 변수에 동시에 할당할 수 있는데 아래 그림에 이와 관련하여 몇 가지 예를 보여주고 있다.
우선 변수의 개수보다 배열의 요수 수가 적으면 나머지 변수들은 값을 할당 받지 못하고 nil 로 초기화 된다.
반대로 변수의 개수보다 배열의 요소 수가 많을 때는 남는 배열의 요소들은 변수에 할당되지 못하고 버려지게 된다.
그런데 이러한 경우에 변수 앞에 '*' 을 붙여주면 버려지는 원소 없이 배열 객체로 해당 변수에 할당되게 된다.
>> a, b = [1]
=> [1]
>> a
=> 1
>> b
=> nil
>> a, b = [1,2,3]
=> [1, 2, 3]
>> a
=> 1
>> b
=> 2
>> a, *b = [1,2,3]
=> [1, 2, 3]
>> a
=> 1
>> b
=> [2, 3]
이제 문제와 답을 만들 수 있게 되었으니 문제를 보여주고 사용자에게 답을 입력받아 정답인지 오답인지 확인하는 메서드를 만들어보자. 아래처럼 exam이라는 메서드를 하나 만들었고 메서드 안에서 make_quiz 메서드를 호출하여 문제와 답을 하나 가져왔다. 그리고 문제를 화면에 보여줄 때 개행문자 없이 출력하기 위해 puts 대신 print 메서드를 사용하였다.
사용자가 아무것도 입력하지 않고 그냥 엔터를 눌렀거나 또는 숫자가 아닌 문자를 입력했을 경우 모두 to_i 메서드는 0을 돌려주게 되어 오답이 된다. 문제가 구구단이 아니라 뺄쎔 등 실제 0 이 정답일 수도 있는 경우도 있기 때문에 사용자 입력에 대한 검사를 조금 더 확실하게 하는 게 좋을 수 있다.
?> def make_quiz
?> n1 = rand(2..9)
?> n2 = rand(1..9)
?> ["#{n1} x #{n2} = ", n1 * n2]
>> end
=> :make_quiz
>>
?> def exam
?> question, answer = make_quiz
?> print question
?> input = STDIN.gets
?> if answer == input.to_i
?> puts "정답"
?> else
?> puts "오답"
?> end
>> end
=> :exam
>> exam
8 x 5 = 40
정답
=> nil
>> exam
6 x 6 = 30
오답
=> nil
이제 남은 건 정해진 시간 동안 문제를 풀도록 하고 시간이 다 되면 최종 점수를 보여주는 일만 남았다.
아래 코드를 보면 정답을 맞춘 수를 카운트하기 위해 exam 메서드를 정답이면 true를 오답이면 false를 돌려주도록 수정했다. if 조건절에서 exam 메서드를 바로 호출하기 때문에 그 결과가 true 면 correct_cnt 변수의 값을 1 증가시키고 화면에 '정답' 을 출력하게 된다.
최종적으로 주어진 시간(여기선 10초로 고정해 두었다.)이 지나면 총 문제 수와 맞춘 문제 수를 보여주고 프로그램은 종료하게 된다.
require 'timeout'
def make_quiz
n1 = rand(2..9)
n2 = rand(1..9)
["#{n1} x #{n2} = ", n1 * n2]
end
def exam
question, answer = make_quiz
print question
input = STDIN.gets
return answer == input.to_i
end
quiz_cnt = 0
correct_cnt = 0
begin
Timeout.timeout(10) do
while true
quiz_cnt += 1
if exam
correct_cnt += 1
puts "정답"
else
puts "오답"
end
end
end
rescue
end
puts "\n총 #{quiz_cnt}문제 중 #{correct_cnt}문제를 맞췄습니다."
프로그램을 여러 번 반복해서 실행하다 보면 결과가 틀리게 나오는 경우가 있다. 특히 내가 정답을 맞춘 횟수보다 하나 적게 나올 수 있는데 그 이유는 이미 시간이 종료되었지만 프로그램은 종료되지 않고 입력을 끝까지 마칠 수 있기 때문이다.
그 이유는 타임아웃이 되기 직전에 이미 키보드 입력이 시작되었다면 다른 작업(여기선 Timeout::Error 예외 발생)이 진행되지 못하고 블로킹되기 때문이다. 어쨌거나 이러한 경우에 내가 실제 정답을 입력하였다고 해도 이미 시간이 지난 후 입력한 것이기 때문에 정답을 맞춘 횟수가 올라가지 않는 것이 맞다.
이것을 빼더라도 결과를 틀리게 할 수 있는 버그가 하나 있는데 그것은 사용자에게 실제 문제를 보여주기도 전에 미리 quiz_cnt 변수의 값을 증가시킨다는 것이다.
다음 번 글에서는 클래스를 사용해 코드를 변경해 보자.
See you again~~