(C++) 스마트팩토리 - 25

내 이름 안찬찬·2023년 2월 26일
0
post-thumbnail

야구게임

  • 사용자는 1부터 9까지의 숫자 중 3개의 숫자를 뽑아 입력 (중복 X)
  • 컴퓨터는 랜덤으로 숫자 3개 저장 (중복 X)
  • 숫자의 자리와 값이 모두 같으면 strike
  • 자리는 다르지만 3개의 숫자 중 포함 되어 있으면 ball

첫 작성 코드

#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;

int main() {
  srand(time(NULL));          // 시간을 기준으로 난수 생성
  const int number = 3;       // 상수 선언
  int comp_num[number] = {};  // 컴퓨터가 뽑은 숫자 저장 배열
  int user_num[number] = {};  // 사용자가 숫자를 저장 배열
  int strike = 0;             // 스트라이크 개수 저장 변수
  int ball = 0;               // 볼 개수 저장 변수
  int count = 0;              // 시도한 횟수를 저장할 변수

  // 랜덤으로 3개의 숫자를 뽑아 comp_num 배열에 저장 (중복 없이)
  for (int i = 0; i < number; i++) {
    bool found = true;
    while (found) {
      found = false;
      comp_num[i] = rand() % 9 + 1;  // 1~9 사이의 난수 생성
      for (int j = 0; j < i; j++) {
        if (comp_num[j] == comp_num[i]) {
          found = true;
          break;
        }
      }
    }
  }

  cout << "숫자 야구 게임을 시작합니다!" << endl;
  while (true) {
    // 사용자에게 3개의 숫자 입력 받음
    cout << "1~9 사이의 숫자 3개를 입력하세요 (이외의 숫자: 종료)\n ";
    cin >> user_num[0] >> user_num[1] >> user_num[2];

    // 입력한 숫자가 1~9 범위 내의 수인지 확인
    bool valid = true;
    for (int i = 0; i < number; i++) {
      if (user_num[i] < 1 || user_num[i] > 9) {
        valid = false;
        break;
      }
    }
    if (!valid) {
      cout << "게임을 종료합니다." << endl;
      break;
    }

    // 스트라이크와 볼의 개수
    for (int i = 0; i < number; i++) {
      if (user_num[i] == comp_num[i]) {
        strike++;
      } else {
        for (int j = 0; j < number; j++) {
          if (i != j && user_num[i] == comp_num[j]) {
            ball++;
          }
        }
      }
    }

    count++;
    cout << "결과: " << strike << " 스트라이크, " << ball << " 볼" << endl;

    if (strike >= 3) {
      cout << "축하합니다! " << count << "회만에 정답을 맞추셨습니다." << endl;
      break;
    }
  }
  return 0;
}

개선 사항

  1. 변수명 개선
comp_num, user_num: 
무엇을 의미하는지 알기 어려우므로 number를 포함한 의미 있는 이름으로 변경

strike, ball:
무엇을 의미하는지는 코드를 분석해야 알 수 있으므로,
각각 strike_count, ball_count로 변경
  1. 범위 상수화
1~9 범위의 숫자가 여러 곳에서 사용되므로,
이를 상수로 정의하여 가독성을 높임
  1. 람다식 활용
컴퓨터가 숫자를 뽑을 때, 중복이 없도록 반복문을 사용하는 대신에
람다식을 활용하여 더 간결하게 구현할 수 있음
  1. 함수 분리
게임을 실행하는 부분과 스트라이크와 볼을 계산하는 부분을 분리하여 가독성을 높임

수정한 코드

#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;

const int NUMBER_OF_DIGITS = 3;  // 상수 선언
const int MIN_NUMBER = 1;
const int MAX_NUMBER = 9;

void generateRandomNumbers(int comp_num[]) {
  srand(time(NULL));  // 시간을 기준으로 난수 생성

  // 랜덤으로 NUMBER_OF_DIGITS개의 숫자를 뽑아 comp_num 배열에 저장 (중복 없이)
  auto generateNumber = []() {
    return rand() % (MAX_NUMBER - MIN_NUMBER + 1) + MIN_NUMBER;
  };
  bool found;
  for (int i = 0; i < NUMBER_OF_DIGITS; i++) {
    do {
      found = false;
      comp_num[i] = generateNumber();
      for (int j = 0; j < i; j++) {
        if (comp_num[j] == comp_num[i]) {
          found = true;
          break;
        }
      }
    } while (found);
  }
}

void playBaseball(int comp_num[]) {
  int user_num[NUMBER_OF_DIGITS] = {};  // 사용자가 숫자를 저장 배열
  int strike_count = 0;                 // 스트라이크 개수 저장 변수
  int ball_count = 0;                   // 볼 개수 저장 변수
  int count = 0;                        // 시도한 횟수를 저장할 변수

  cout << "숫자 야구 게임을 시작합니다!" << endl;
  while (true) {
    // 사용자에게 NUMBER_OF_DIGITS개의 숫자 입력 받음
    cout << MIN_NUMBER << "~" << MAX_NUMBER << " 사이의 숫자 "
         << NUMBER_OF_DIGITS << "개를 입력하세요 (이외의 숫자: 종료)\n ";
    cin >> user_num[0] >> user_num[1] >> user_num[2];

    // 입력한 숫자가 MIN_NUMBER~MAX_NUMBER 범위 내의 수인지 확인
    bool valid = true;
    for (int i = 0; i < NUMBER_OF_DIGITS; i++) {
      if (user_num[i] < MIN_NUMBER || user_num[i] > MAX_NUMBER) {
        valid = false;
        break;
      }
    }
    if (!valid) {
      // 게임 종료 안내 메시지 출력
      cout << "게임을 종료합니다." << endl;
      break;
    }

    // 스트라이크와 볼의 개수 계산
    strike_count = ball_count = 0;
    for (int i = 0; i < NUMBER_OF_DIGITS; i++) {
      if (user_num[i] == comp_num[i]) {
        strike_count++;
      } else {
        for (int j = 0; j < NUMBER_OF_DIGITS; j++) {
          if (i != j && user_num[i] == comp_num[j]) {
            ball_count++;
          }
        }
      }
    }

    // 시도한 횟수 증가 및 결과 출력
    count++;
    cout << "결과: " << strike_count << " 스트라이크, " << ball_count << " 볼"
         << endl;

    if (strike_count == NUMBER_OF_DIGITS) {
      // 축하 메시지와 시도한 횟수 출력
      cout << "축하합니다! " << count << "회만에 정답을 맞추셨습니다." << endl;
      break;
    }
  }
}

int main() {
  int comp_num[NUMBER_OF_DIGITS] = {};  // 컴퓨터가 숫자를 저장할 배열 선언

  generateRandomNumbers(comp_num);  // 컴퓨터 숫자 랜덤으로 생성

  playBaseball(comp_num);  // 숫자 야구 게임 실행

  return 0;
}

주요 기술


1. srand함수와 time함수를 이용한 난수 생성

srand(time(NULL));  // 시간을 기준으로 난수 생성

srand 함수는 난수를 생성하기 위한 함수로, seed 값을 설정하여 난수를 생성한다.

seed 값이 같으면 같은 난수를 생성하기 때문에, srand 함수를 사용하기 전에 seed 값을 변화시켜 주었다.

이 코드에서는 시간을 seed 값으로 사용하여 매번 다른 난수를 생성하도록 구현


  1. 람다 함수 (Lamda Function)
auto generateNumber = []() {
  return rand() % (MAX_NUMBER - MIN_NUMBER + 1) + MIN_NUMBER;
};

람다 함수는 익명 함수로, 함수를 선언하지 않고 바로 사용할 수 있다.

이 코드에서는 중복 없이 랜덤으로 숫자를 뽑기를 구현하기 위해 람다 함수를 사용

generateNumber는 MIN_NUMBER ~ MAX_NUMBER 사이의 난수를 반환하는 함수


3. 중복 없이 랜덤으로 컴퓨터 숫자 뽑기

for (int i = 0; i < NUMBER_OF_DIGITS; i++) {
  do {
    found = false;
    comp_num[i] = generateNumber();
    for (int j = 0; j < i; j++) {
      if (comp_num[j] == comp_num[i]) {
        found = true;
        break;
      }
    }
  } while (found);
}

generateRandomNumbers 함수에서는 중복 없이 NUMBER_OF_DIGITS개의 숫자를 뽑아 comp_num 배열에 저장

do-while문을 이용하여, generateNumber 함수를 이용해 랜덤으로 숫자를 뽑은 뒤, 이전에 뽑은 숫자들과 중복되는지 확인하여 중복되지 않은 숫자를 저장하도록 구현하였다.


4. 스트라이크와 볼 판정

strike_count = ball_count = 0;
for (int i = 0; i < NUMBER_OF_DIGITS; i++) {
  if (user_num[i] == comp_num[i]) {
    strike_count++;
  } else {
    for (int j = 0; j < NUMBER_OF_DIGITS; j++) {
      if (i != j && user_num[i] == comp_num[j]) {
        ball_count++;
      }
    }
  }
}

게임의 로직은 while문을 사용하여 반복되는데, while문 내부에서 사용되는 do-while문과 for문이 중요하다.

do-while문은 사용자가 입력한 숫자가 MIN_NUMBER~MAX_NUMBER 범위 내의 수인지를 체크하여 유효한 입력이 이루어질 때까지 반복한다.

for문은 스트라이크와 볼의 개수를 계산하는데 사용된다. NUMBER_OF_DIGITS(현재는 3)만큼 반복하면서 사용자가 입력한 숫자와 컴퓨터가 생성한 숫자를 비교하고, 스트라이크와 볼의 개수를 계산한다.

for, do-while: for문은 반복 횟수를 미리 지정하여 반복을 수행하고, do-while문은 조건을 먼저 검사하지 않고 먼저 반복을 수행한 후 조건을 검사하여 반복 여부를 결정한다.


5. 함수

generateRandomNumbers(): 컴퓨터가 랜덤하게 생성한 숫자를 배열에 저장

playBaseball(): 사용자가 입력한 숫자와 컴퓨터가 생성한 숫자를 비교하여 스트라이크와 볼의 개수를 계산하고 게임의 진행 상황을 출력




마무리

오랜만에 람다함수에 대해 떠올리게 되었고, 찾아보고 코드를 수정하는데 사용했다.

학교에서 배웠던 람다함수는 java에서 배웠던 기억이지만!

학교에서 배운것들이 하등 쓸모없지 않다고 느껴져서 좋았다.

그리고 계속해서 함수를 생성해 main함수 부분을 간결하게 만들고,
유지보수를 생각하고 가독성을 높이는데 집중하며 코드를 수정하다보니
재미도 느껴진다.

몰아쳐서 벨로그를 썼기에 더 쓸 말이 없다..
끄읕..!

profile
스마트팩토리 개발자가 되기 위한 □□ !!

0개의 댓글