트랙
/
Cairo
Cairo
/
연습 문제
/
롤플레잉 게임
롤플레잉 게임

롤플레잉 게임

학습 연습 문제

소개

널 참조

다른 프로그래밍 언어(C/C++, Python, Java, Ruby, Lisp 등)를 사용해 본 적이 있다면, null이나 nil을 접해 봤을 가능성이 커요. 이런 언어에서는 null이나 nil로 특정 변수에 값이 없다는 것을 나타내요. 하지만 이 때문에 null 변수를 실수로 사용하는 일이 쉽고도 자주 일어나요. 짐작할 수 있듯이, 존재하지 않는 함수를 호출하거나 없는 값에 접근하려고 하면 온갖 버그와 충돌로 이어질 수 있어요. null을 만든 사람은 심지어 이것을 자신의 '10억 달러짜리 실수'라고 부르기까지 했어요.

Option 타입

이런 문제를 피하기 위해, Cairo의 변수는 Option 열거형을 사용해서 값이 없다는 것을 안전하게 표현할 수 있어요. 이 열거형에는 두 가지 변형이 있어요. None은 Cairo에서 null에 해당하는 값이고, Some(T)에서 T는 아무 타입의 값이에요.

다음과 같아요:

enum Option<T> {
    None,
    Some(T),
}

Option은 널 참조가 일으킬 수 있는 문제를 막아 주는 안전 계층이라고 생각하면 돼요. 그러면서도 널 참조가 가진 개념적 유용성은 그대로 간직해요.

Option 사용하기

변수에 None을 설정하는 것은 꽤 간단해요:

let nothing: Option<u32> = Option::None;  // Variable nothing is set to None

하지만 Option 타입이 값을 담기를 원한다면, 그 값을 직접 대입할 수는 없어요. Option 타입 변수와, 예를 들어 i32 타입 변수는 서로 같지 않아요. Some을 사용해야 해요:

let wrong_way: Option<i32> = -4; // This will not work

let right_way: Option<i32> = Option::Some(-4); // This will work
let another_right_way = Option::Some(-4); // Compiler infers that this is Option<i32>

같은 이유로 다음 코드도 작동하지 않아요:

let number = 47;
let option_number = Option::Some(15);

let compile_error = number + option_number; // Cannot add an i32 and an Option<i32> - they are of different types

Some이 담고 있는 값을 꺼내려면, 그 값이 존재하는지 먼저 확인해야 해요:

let mut some_words = Option::Some("choose something to say"); // some_words set to something

match some_words {
    Option::Some(str) => println!("Here, we will {}", str),
    Option::None => println!("I've got nothing to say"),
} // Prints, "Here, we will choose something to say"

some_words = Option::None; // some_words now set to None

// Exactly the same match block as above
match some_words {
    Option::Some(str) => println!("Here, we will {}", str),
    Option::None => println!("I've got nothing to say"),
} // Prints, "I've got nothing to say"

Cairo에는 Option에 담긴 값을 확인하고 꺼내는 데 쓸 수 있는 도구가 match 말고도 더 있어요. 하지만 이제 match는 익숙할 거예요.

Cairo가 널 참조 대신 Option을 사용하는 이유를 보여 주는 예라고 생각해요. 핵심은 Option 변수가 Some인지(그렇다면 그 안에 담긴 값을 꺼내서 사용하면 돼요), 아니면 None인지를 반드시 확인해야 한다는 거예요. 그렇지 않으면 프로그램이 컴파일되지 않아요. 컴파일러가 null로부터 코드를 안전하게 지켜 줘요.

지침

지금 롤플레잉 게임을 구현하고 있어요. 플레이어의 캐릭터는 다음과 같이 표현해요.

pub struct Player {
    health: u32,
    mana: Option<u32>,
    level: u32,
}

이 게임의 플레이어는 레벨 10에 도달해야 마나 풀을 사용할 수 있고, 이 마나 풀로 주문을 시전할 수 있어요. 그 전까지 플레이어의 마나는 Option::None이에요.

이 게임에서는 되살리기 기능과 주문 시전 기능, 두 가지를 구현하고 있어요.

revive 메서드는 플레이어가 정말로 죽었는지(체력이 0이 되었는지) 확인하고, 죽었다면 체력이 100인 새로운 Player 인스턴스를 반환해야 해요. revive 메서드는 플레이어의 레벨을 그대로 유지해야 해요. 플레이어의 레벨이 10 이상이면 마나 100을 가진 상태로 되살아나요. 플레이어의 레벨이 10 미만이면 마나는 Option::None이어야 해요.

let dead_player = Player { health: 0, mana: Option::None, level: 2 };
dead_player.revive()
// Returns Player { health: 100, mana: Option::None, level: 2 }

체력이 1 이상인 플레이어에 대해 revive 메서드를 호출하면, 메서드는 Option::None을 반환해야 해요.

let alive_player = Player { health: 1, mana: Option::Some(15), level: 11 };
alive_player.revive()
// Returns Option::None

cast_spell 메서드는 플레이어에 대한 가변 참조와, 주문에 드는 마나를 나타내는 mana_cost 매개변수를 받아요. 시전한 주문이 입히는 피해량을 반환해요.

  • 플레이어가 마나 풀을 사용할 수 없다면, 주문을 시전하려고 할 때 주문의 마나 비용만큼 체력이 줄어들어야 해요. 반환되는 피해량은 0이어야 해요.

    let not_a_wizard_yet = Player { health: 79, mana: Option::None, level: 9 };
    assert_eq!(not_a_wizard_yet.cast_spell(5), 0)
    assert_eq!(not_a_wizard_yet.health, 74);
    assert_eq!(not_a_wizard_yet.mana, Option::None);
    
  • 플레이어에게 마나 풀이 있지만 마나가 부족하다면, 메서드는 마나 풀에 영향을 주어서는 안 돼요. 반환되는 피해량은 0이어야 해요.

    let low_mana_wizard = Player { health: 93, mana: Option::Some(3), level: 12 };
    assert_eq!(low_mana_wizard.cast_spell(10), 0);
    assert_eq!(low_mana_wizard.health, 93);
    assert_eq!(low_mana_wizard.mana, Option::Some(3));
    
  • 그 외의 경우에는 주문이 성공적으로 시전돼요. mana_cost는 플레이어의 마나 풀에서 차감돼요. 반환되는 피해량은 주문의 마나 비용의 두 배여야 해요.

    let wizard = Player { health: 123, mana: Option::Some(30), level: 18 };
    assert_eq!(wizard.cast_spell(10), 20);
    assert_eq!(wizard.health, 123);
    assert_eq!(wizard.mana, Option::Some(20));
    

즐겁게 해봐요!

GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Cairo Exercism

롤플레잉 게임 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Cairo 트랙을 개념 25개연습 문제 68개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.