트랙
/
Crystal
Crystal
/
연습 문제
/
체스 게임
체스 게임

체스 게임

학습 연습 문제

소개

레인지는 두 값 사이의 구간을 나타내요. 레인지를 지원하는 가장 흔한 타입은 Int, Char, String이에요. 레인지는 컬렉션을 빠르게 만들거나, 문자열을 자르거나, 어떤 값이 레인지 안에 있는지 확인하거나, 반복할 때 등 여러 상황에서 쓸 수 있어요. 레인지는 .. 또는 ... 레인지 연산자로 만들어요. 각각 끝 값을 포함하는 레인지와 포함하지 않는 레인지예요.

1..5  # A range containing 1, 2, 3, 4, 5
1...5 # A range containing 1, 2, 3, 4

레인지 연산자가 두 개인 이유는 끝 값을 포함하는 레인지와 포함하지 않는 레인지를 만들기 위해서예요. 이건 예를 들어 0부터 시작하는 인덱스를 다룰 때 유용해요.

레인지는 Range 초기화 메서드로도 만들 수 있어요.

Range.new(1, 5) # A range containing 1, 2, 3, 4, 5
Note

Crystal에서 .. 또는 ... 레인지 연산자로 레인지를 만들고 그 레인지에 메서드를 호출하고 싶을 때는, 레인지를 괄호로 감싸야 해요. 그러지 않으면 레인지 연산자의 두 번째 인자에 메서드가 호출되기 때문이에요.

(1..5).size # => 5
1..5.size # => Error: undefined method 'size' for Int32

부분 문자열 가져오기

문자열을 자르고 싶을 때 레인지 연산자로 부분 문자열을 얻을 수 있어요. 즉, 부분 문자열의 시작과 끝 인덱스로 레인지를 만드는 거예요.

"Hello World"[0..4] # => "Hello"
"Hello World"[6..10] # => "World"

음수 인덱스를 쓰면 문자열의 끝에서부터 부분 문자열을 얻을 수도 있어요.

"Hello World"[-5..-1] # => "World"
"Hello World"[6..-4] # => "Wo"

레인지 메서드

레인지에는 레인지를 다루는 데 쓸 수 있는 여러 메서드가 있어요. 예를 들어 레인지에 있는 모든 값의 합을 구하거나, 레인지가 어떤 값을 포함하는지 확인할 수 있어요.

메서드 설명 예제
sum 레인지에 있는 모든 값의 합을 반환해요 (1..5).sum # => 15
size 레인지의 크기를 반환해요 (1..5).size # => 5
includes? 레인지가 주어진 값을 포함하면 true를, 그렇지 않으면 false를 반환해요 (1..5).includes?(3) # => true

끝이 없는 레인지와 시작이 없는 레인지

레인지는 끝이 없거나 시작이 없을 수 있어요. 끝이 없거나 시작이 없는 레인지는 시작 값이나 끝 값이 nil이지만, 레인지를 정의할 때 nil은 생략할 수 있어요.

시작이 없거나 끝이 없는 레인지는 예를 들어 문자열을 처음부터 또는 끝까지 자르고 싶을 때 유용해요.

"Hello World"[0..] # => "Hello World"
"Hello World"[4..] # => "o World"
"Hello World"[..5] # => "Hello"
Caution

컬렉션에 사용하지 않는다면, 끝이 없는 레인지는 조심하지 않을 경우 끝없는 시퀀스를 만들 수 있어요.

Char 레인지

Char는 레인지에 쓸 수 있어서 두 문자 사이의 문자 구간을 얻을 수 있어요. 예를 들어 알파벳을 얻고 싶을 때 유용해요.

'a'..'z' # A range containing ['a', 'b', 'c', ..., 'z']

String 레인지

String도 레인지에 쓸 수 있어서 두 문자열 사이의 문자열 구간을 얻을 수 있어요. 다만 문자열 레인지에 여러 문자가 쓰이면 동작이 Char와는 조금 달라요. 더 복잡한 문자열 레인지를 다룰 때는 동작이 헷갈릴 수 있으니 주의해야 해요.

("aa".."az") # A range containing ["aa", "ab", "ac", ..., "az"]

지침

체스 애호가라면, 자신만의 체스 게임을 만들어 보고 싶을 거예요. 네, 이미 온라인에 체스 구현이 많이 있겠지만, 직접 만든 것은 독특할 거예요!

기물의 기본 이동 체계를 구현하는 것부터 시작해요.

체스 게임은 가로 여덟 칸, 세로 여덟 칸인 보드에서 진행돼요. 각 칸은 문자와 숫자로 구분해요.

1. 랭크와 파일 범위 정의하기

게임은 보드의 랭크를 저장해야 해요. 랭크는 보드의 행이며, 1부터 8까지 번호가 매겨져 있어요.

게임은 보드의 파일도 저장해야 해요. 파일은 보드의 열이며, 문자 A부터 H로 구분해요.

각각 랭크와 파일의 범위를 저장하는 Chess::RANKS와 Chess::FILES 상수를 정의해요.

Chess::RANKS
# => 1..8

Chess::FILES
# => 'A'..'H'

2. 칸이 유효한지 확인하기

게임은 칸이 유효한지 확인해야 해요. 랭크와 파일이 각각 랭크와 파일의 범위 안에 있으면 그 칸은 유효해요.

랭크의 정수를 담는 rank와 파일의 문자를 담는 file 인자를 받는 Chess.valid_square? 메서드를 정의해요. 이 메서드는 랭크와 파일이 랭크와 파일의 범위 안에 있으면 true를, 그렇지 않으면 false를 반환해야 해요.

Chess.valid_square?(1, 'A')
# => true

3. 플레이어의 닉네임 가져오기

게임은 플레이어의 닉네임을 가져와야 해요. 닉네임은 플레이어의 이름의 처음 두 문자와 성의 마지막 두 문자예요. 닉네임은 대문자로 표기해야 해요.

플레이어의 이름을 담는 first_name 문자열과 플레이어의 성을 담는 last_name 문자열 인자를 받는 Chess.nickname 메서드를 정의해요. 이 메서드는 플레이어의 닉네임을 대문자로 변환한 문자열로 반환해야 해요.

Chess.nickname("John", "Doe")
# => "JOOE"

4. 이동 메시지 만들기

게임은 어떤 플레이어가 어떤 칸으로 이동했는지 알려 주는 이동 메시지를 만들어야 해요. 메시지에는 플레이어의 닉네임과 이동한 칸을 사용해요. 또한 게임은 칸의 파일과 랭크가 파일과 랭크의 범위 안에 있는지 확인해서 이동이 유효한지 판단해야 해요.

이동이 유효하면 메시지는 "{nickname} moved to {square}"여야 해요. 이동이 유효하지 않으면 메시지는 "{nickname} attempted to move to {square}, but that is not a valid square"여야 해요.

플레이어의 이름을 담는 first_name 문자열, 플레이어의 성을 담는 last_name 문자열, 플레이어가 이동한 칸을 담는 square 문자열을 인자로 받는 Chess.move_message 메서드를 정의해요. 이 메서드는 이동 메시지를 문자열로 반환해야 해요.

Chess.move_message("John", "Doe", "A1")
# => "JOOE moved to A1"
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Crystal Exercism

체스 게임 문제를 시작해 볼 준비가 됐나요?

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