만들어볼 게임은 2가지다.
(1) 가위바위보
(2) 숫자 맞히기
둘 다 공통적으로 rand crate를 사용한다.
// Cargo.toml
[package
]
name
= "rps-game" # 참고로 여긴 하이픈으로 써도 코드 내에선 언더바로 써야 한다
version
= "0.1.0"
edition
= "2021"
[dependencies
]
rand
= "0.8.4"
미리 말해둘 점은 가위바위보든 RPS든
셋 다 123이라고 가정했을 때
1 < 2
2 < 3
3 < 1
이런 관계가 성립한다는 것이다
그러니 선택지를 숫자로 제시하고
해당 숫자의 비교를 하면 될 것이다.
우선 틀을 잡아보겠다
use rand
::Rng
; // rand crate의 Rng 사용 (Random number generator)
fn
main() {
// startup introduction
println
!("Welcome to RPS Game.");
// game loop
loop
{
// write game logic here
}
// shutdown message
println
!("Goodbye.");
}
게임 루프에 쓸 내용은 다음과 같다.
1. 123의 선택지를 제시할 것. (q는 종료)
2. 컴퓨터도 숫자를 정할 것.
3. 비교 후 승패를 판별할 것.
ctrl + c로 강제 종료가 되긴 하지만,
그것보단 정상적인 종료 방법을 제공해주는 것이 좋다.
use rand
::Rng
; // rand crate의 Rng 사용 (Random number generator)
use std
::io
::Write
; // flush를 위해 필요
fn
main() {
// startup introduction
println
!("Welcome to RPS Game.");
// game loop
loop
{
// String for getting user input
let mut user_input
= String
::new();
// show input guide
print
!("Choose your option: (1) Rock, (2) Paper, (3) Scissors, (q) quit: ");
std
::io
::stdout().flush().unwrap();
// get user input
std
::io
::stdin().read_line(&mut user_input
).unwrap();
// remove the newline character
user_input
= user_input
.trim().to_string();
// if user input is q, then quit the game
if user_input
== "q" {
break;
}
// convert user input to integer
let user_input
: i32
= match user_input
.parse() {
Ok(num
) => num
,
Err(_
) => {
continue;
}
};
// if user input is not 1, 2, or 3, then continue the game loop
if user_input
< 1 || user_input
> 3 {
continue;
}
// computer input
}
// shutdown message
println
!("Goodbye.");
}
여기까지가 딱 유저 입력을 받는 부분이다.
flush, read_line은 3편 사용자 입출력 편에서 설명한 적이 있다.
read_line을 하게 되면 마지막에 엔터가 들어가게 된다.
trim은 그 부분을 잘라낼 수 있다.
trim은 &str을 반환하기 때문에 to_string을 써서 String으로 바꿔줘야 한다.
이제 컴퓨터 입력을 받아보겠다.
let computer_input
: i32
= rand
::thread_rng().gen_range(1..=3);
컴퓨터 입력은 String으로 할 필요 없이 바로 i32로 받으면 된다.
저렇게 하면 1부터 3까지의 숫자가 랜덤으로 나온다.
이제 서로의 선택을 보여준다.
숫자만 띄우면 좀 그러니까, 문자열로 변환을 해준다.
Rock, Paper, Scissors, 이들은 길이가 다르기 때문에
첫 글자를 따서 RPS라고만 짓겠다.
그리고 승패를 미리 판별해 <, =, >, 셋 중 하나를 띄울 것이다.
// computer input
let computer_input
: i32
= rand
::thread_rng().gen_range(1..=3);
// convert to play
let user_play
= match user_input
{
1 => "R",
2 => "P",
_
=> "S",
};
let computer_play
= match computer_input
{
1 => "R",
2 => "P",
_
=> "S",
};
// compare the play
let user_win
; // 초기화는 바로 하지 않아도 된다.
let sign
; // 다만 타입은 일치시켜야 한다.
if user_input
== computer_input
{
sign
= "=";
user_win
= 0;
} else if user_input
== 1 && computer_input
== 3
|| user_input
== 2 && computer_input
== 1
|| user_input
== 3 && computer_input
== 2 {
sign
= ">";
user_win
= 1;
} else {
sign
= "<";
user_win
= -1;
}
// show each other's choices
print
!("[You] {} {} {} [CPU] ", user_play
, sign
, computer_play
);
// show the result
match user_win
{
1 => println
!("You won."),
0 => println
!("Draw."),
_
=> println
!("CPU won."),
}
결과:
Welcome to RPS Game
.
Choose your option
: (1) Rock
, (2) Paper
, (3) Scissors
, (q
) quit
: 1
[You
] R
> S
[CPU
] You won
.
Choose your option
: (1) Rock
, (2) Paper
, (3) Scissors
, (q
) quit
: 2
[You
] P
= P
[CPU
] Draw
.
Choose your option
: (1) Rock
, (2) Paper
, (3) Scissors
, (q
) quit
: 3
[You
] S
< R
[CPU
] CPU won
.
Choose your option
: (1) Rock
, (2) Paper
, (3) Scissors
, (q
) quit
: 4
Choose your option
: (1) Rock
, (2) Paper
, (3) Scissors
, (q
) quit
: asd
Choose your option
: (1) Rock
, (2) Paper
, (3) Scissors
, (q
) quit
: q
Goodbye
.
컴퓨터 플레이가 랜덤으로 나오고 있고,
또 1~3이나 q가 아닌 선택지도 잘 무시하는 모습이다.
이제 허들을 약간 높여 숫자 추측 게임을 만들어 볼 것이다.
코드를 줄이기 위해 다음 함수가 있다고 가정하겠다.
fn
get_string(guide
: &str
) -> String
{
// show guide
print
!("{}", guide
);
std
::io
::stdout().flush().unwrap();
// get input
let mut input
= String
::new();
std
::io
::stdin().read_line(&mut input
).unwrap();
return input
.trim().to_string();
}
가이드를 인자로 넣으면 print와 flush를 해주고,
문자열을 받은 뒤 그것을 리턴해주는 함수다.
이 부분은 main의 밑에 써도 상관없고, 위에 써도 상관없다.
러스트는 함수를 쓰려고 하면 그 함수가 어디에 있는지 알아서 찾아준다.
선언부와 헤더 파일을 만드느라 고생하지 않아도 된다는 점은 러스트의 큰 장점 중 하나다
이제 루프를 만들 건데, 이중으로 만들어야 한다.
첫번째 루프에서 숫자를 하나 정할거고,
두번째 루프에서 맞힐 때까지 라운드를 반복할 것이다.
fn
main() {
// startup introduction
println
!("Welcome to Guessing Game.");
// game loop
loop
{
let secret_number
= rand
::thread_rng().gen_range(1..=100);
// round loop
loop
{
}
}
// shutdown message
println
!("Goodbye.");
}
대략적인 틀은 위와 같다.
여기서 내용을 채우면 다음과 같다.
fn
main() {
// startup introduction
println
!("Welcome to Guessing Game.");
// game loop
'outer
: loop
{
let secret_number
= rand
::thread_rng().gen_range(1..=100);
// round loop
loop
{
let guess
= get_string("Input a number from 1 to 100 (q to quit): ");
// check quit
if guess
== "q" {
break 'outer
;
}
// check parse
let guess
: i32
= match guess
.parse() {
Ok(num
) => num
,
Err(_
) => continue,
};
// check range
if guess
< 1 || guess
> 100 {
continue;
}
if guess
< secret_number
{
println
!("Too small.");
} else if guess
> secret_number
{
println
!("Too big.");
} else {
println
!("Correct.");
break;
}
}
}
// shutdown message
println
!("Goodbye.");
}
q를 입력할 때 바깥 루프를 탈출하기 위해 레이블링을 하였다.
결과:
Welcome to Guessing Game
.
Input a number from
1 to
100 (q to quit
): 50
Too small
.
Input a number from
1 to
100 (q to quit
): 75
Too small
.
Input a number from
1 to
100 (q to quit
): 85
Too big
.
Input a number from
1 to
100 (q to quit
): 80
Too small
.
Input a number from
1 to
100 (q to quit
): 81
Correct
.
Input a number from
1 to
100 (q to quit
): 81
Too big
.
Input a number from
1 to
100 (q to quit
): q
Goodbye
.
맞히기 전까지 수를 잘 유지하다가,
맞힌 후엔 정답인 수가 제대로 바뀌는 모습이다.
q를 입력하였을 때 정상적인 종료도 가능하다.
사용자 입출력, 난수 생성 등을 이용해 간단한 게임을 2개 만들어보았다.
사용자 입출력을 해 보았으니, 다음 5편 때는 파일 입출력을 해 보겠다.
해당 댓글은 삭제되었습니다.
거기가 뭐하는데죠