상세 컨텐츠

본문 제목

[포스코x코딩온] 스마트 팩토리 SW 개발자 과정 3주차 회고 | 31게임, 마방진 만들기

본문

 지난주까지 만발했던 벚꽃이 봄비와 함께 씻겨 나간 뒤, 맞이한 3주차에서는 여태까지 배운 내용을 토대로 프로젝트를 진행하는 시간을 가졌다. 총 7개의 프로젝트를 진행했는데 그 중에서 오늘 정리할 것은 31게임과 마방진이다. 그럼 31게임 부터 해설을 시작해보자.

 

 31게임은 흔히들 알고 있는 서로 1~3 사이의 숫자를 말하면 말한 숫자만큼 1씩 수를 늘려가다가 31을 말하게 되는 사람이 지는 게임이다. 이번에 과제로 나온 31게임의 조건은 다음과 같다. 

 

 1. 컴퓨터와 사람이 번갈아 입력한다. 이때, 최소 1에서 최대 3까지의 숫자를 1회만 입력 가능해야한다.

 2. 입력하는 숫자는 사용자는 본인이 입력한 숫자만큼 입력이 가능해야한다.

 3. 컴퓨터는 랜덤 숫자만큼 입력할 수 있다.

 

 위의 조건을 토대로 코드를 구성해보면 아래와 같다.

 

#include <iostream>
#include <cstdlib>
#include <ctime>

using std::cout;
using std::cin;
using std::endl;
using std::string;

int main(){
    std::srand(time(NULL));
    int gnum = 0; // game number
    int pnum; // cin, player's num
    int cnum; // cpu's num
    bool turn = false; // turn false = player, true = cpu

    while(gnum < 31){ 

        if(turn == false){
            cout << "숫자를 입력해주세요 : ";
            cin >> pnum;
            cout << endl;
            if(pnum == 0 || pnum > 3 || pnum < 0){
                cout << "1~3 사이의 숫자를 입력해주세요" << endl;
            }
            else{
                for(int i = 0; i < pnum; i++){
                    cout << ++gnum << endl; 
                }
                cout << endl;
                turn = true;
            }
        }
        else if(turn == true){
            cout << "컴퓨터가 입력한 숫자입니다!" << endl;
            int rnum = std::rand();  //random num
            cnum = rnum % 3 + 1;

            for(int j = 0; j < cnum; j++){
                cout << ++gnum << endl;
            }
            cout << endl;
            turn = false;
            }        
            
            if(gnum >= 31){
            break;
            }
    }

    // 게임의 결론부
    if(gnum >= 31){
        cout << "게임이 종료되었습니다" << endl;
        if(turn == false){
            cout << "player 승!" << endl;
        }
        else if(turn == true){
            cout << "cpu 승!" << endl;
        }
    }
}

 우선 C++에서는 난수 생성을 위해 #include <cstdlib>와 #include <ctime> 을 추가해줘야한다. cstdlib의 경우에는 난수 생성함수가 여기에 들어있기 때문에 불러오는 것이고 ctime의 경우에는 난수를 초단위로 생성하게 만들기 위해 불러와 주는 것이다. 이렇게 둘을 추가했다면 이제 난수 생성함수를 사용할 준비가 끝났다. 난수 생성 함수 사용을 위해 main 함수 안에 std::srand(time(NULL))을 써준다. 그리고 난수 사용할 곳에 난수를 저장할 변수 rnum을 선언하고 동시에 std::rand()로 초기화 해준다. 그리고 이때 난수의 범위 설정을 위해 rnum을 3으로 나눈 나머지 값을 받아온다. 이 과정에서 0이 발생하는 것을 차단하기 위해 +1을 해서 1~3까지의 범위를 설정해준다.

else if(turn == true){
            cout << "컴퓨터가 입력한 숫자입니다!" << endl;
            int rnum = std::rand();  //random num
            cnum = rnum % 3 + 1;

 이렇게하면 이제 난수 생성 부분은 끝났다. 이제 사용자와 컴퓨터의 차례를 어떻게 넘어가게 해줄지가 중요하다. 이걸 위해서 bool 자료형 변수인 turn을 사용했다. 아래 코드를 보면 이해가 더 쉬울 것이다. 

int main(){
    std::srand(time(NULL));
    int gnum = 0; // game number
    int pnum; // cin, player's num
    int cnum; // cpu's num
    bool turn = false; // turn false = player, true = cpu

    while(gnum < 31){ 

        if(turn == false){
            cout << "숫자를 입력해주세요 : ";
            cin >> pnum;
            cout << endl;
            if(pnum == 0 || pnum > 3 || pnum < 0){
                cout << "1~3 사이의 숫자를 입력해주세요" << endl;
            }
            else{
                for(int i = 0; i < pnum; i++){
                    cout << ++gnum << endl; 
                }
                cout << endl;
                turn = true;
            }
        }
        else if(turn == true){
            cout << "컴퓨터가 입력한 숫자입니다!" << endl;
            int rnum = std::rand();  //random num
            cnum = rnum % 3 + 1;

            for(int j = 0; j < cnum; j++){
                cout << ++gnum << endl;
            }
            cout << endl;
            turn = false;
            }

 여기서 while문 안의 if문을 보면 else일 때 값을 출력하고 turn을 ture로 바꾸게 된다. 그러면 if문의 조건이 불충족되고 else if문의 조건이 충족되면서 컴퓨터의 차례가 오게 되는것이다. 마찬가지로 else if 문에서도 마지막에 turn을 false로 바꿔주면서 사용자의 차례가 오도록하는 것을 볼 수 있다.

 

 이렇게 해서 사용자는 1~3까지의 값만을 입력하고 컴퓨터는 1~3까지의 값 중 랜덤으로 숫자를 입력하는 31 게임 프로그램이 완성 되었다. 출력 결과는 아래와 같다. 

 사용자와 컴퓨터가 순서를 번갈아 가며 1~3까지 숫자를 입력 받고 컴퓨터는 무작위로 숫자를 입력하는 것을 볼 수 있다. 그리고 31을 입력쪽이 진다는 것까지 정상적으로 출력되는 것을 확인할 수 있다. 여기까지가 랜덤함수를 활용한 31게임에 대한 해설이었다.

 

 다음은 마방진에 대한 해설로 넘어가보자. 마방진은 1에서 N제곱 까지의 수를 정사각형으로 배열해 가로, 세로, 대각의 합계가 모두 같도록 만든것이다. 그 중에서도 홀수 N을 입력 받을 때 마방진을 생성하는 코드를 구성하는 것이 과제였다. 과제를 수행하기 위한 마방진의 규칙은 아래와 같다.

 

 1.  1은 첫 행의 가운데에 위치한다.

 2. 우상단으로 갈 수록 숫자가 1씩 늘어난다.

 3. 우상단으로 이동하는 도중 이미 칸이 채워져 있으면 바로 아래 칸에 다음 숫자가 채워진다.

 4. 첫번째 행에서 우상단으로 이동할 때는 마지막 행의 다음 열로 이동한다.

 5. 마지막 열에서 우상단으로 이동할 때는 첫번째 열의 이전 행으로 이동한다.

 6. 첫번째 행의 마지막 열에서는 우상단으로 이동하는 것이 아니라 바로 아래칸으로 이동한다.

 

 다음은 위 조건을 만족하는 마방진을 생성하는 코드이다.

#include <iostream>

using std::cout;
using std::cin;
using std::string;
using std::endl;

int main(){
    int num;
    
     while(1){
        cout << "홀수를 입력해주세요 : ";
        cin >> num;
        if(num %2 == 0){
            cout << "잘못된 수 입니다! 홀수를 입력해주세요!!" << endl;
        }
        else {
            break;
        }
    }
    
    int** arr = new int* [num];
    for(int i = 0; i < num; i++){
        arr[i] = new int [num];
    }

    int k, nmg, x = 0, y = num/2;
   

    for(k = 1; k < num * num; k++){
        arr[x][y] = k;
        nmg = k % num;
        if(nmg == 0){ //입력받은 홀수의 배수일 때 한칸 아래로 이동
            x ++;
        }
        else if(--x < 0){ // 1행에서 마지막 행으로 이동
            x = num - 1;
        } if(++y == num){ // 첫번째 열의 이전 행으로 이동
            y = 0;
        }
    }
    //출력부
    for(int i = 0; i < num; i++){
        for(int j = 0; j < num; j++){
            cout << arr[i][j] << " ";
        }
        cout << endl;
    }
    
    for(int i = 0; i < num; i++){
        delete[] arr[i];
    }
    delete[] arr;

    return 0;
}

 위의 조건들을 충족하기 위해 동적 배열을 활용해 마방진을 생성했다. 조건들에 맞춰 구성한 코드를 보며 해설을 해보도록하겠다.

int k, nmg, x = 0, y = num/2;
   

    for(k = 1; k < num * num; k++){
        arr[x][y] = k;
        nmg = k % num;
        if(nmg == 0){ //입력받은 홀수의 배수일 때 한칸 아래로 이동
            x ++;
        }
        else if(--x < 0){ // 1행에서 마지막 행으로 이동
            x = num - 1;
        } if(++y == num){ // 첫번째 열의 이전 행으로 이동
            y = 0;
        }
    }

 1. 1은 첫행의 가운데에 위치한다. -> x와 y 값을 각각 0과 num/2로 초기화 해서 1을 1행의 중앙에 위치 시킨다.

int k, nmg, x = 0, y = num/2;

    for(k = 1; k < num * num; k++){
        arr[x][y] = k;

 

 

 4. 첫번째 행에서 우상단으로 이동할 때는 마지막 행의 다음 열로 이동한다.

else if(--x < 0){ // 1행에서 마지막 행으로 이동
            x = num - 1;
}

 

 5. 마지막 열에서 우상단으로 이동할 때는 첫번째 열의 이전 행으로 이동한다.

if(++y == num){ // 첫번째 열의 이전 행으로 이동
            y = 0;
}

 

 6. 첫번째 행의 마지막 열에서는 우상단으로 이동하는 것이 아니라 바로 아래칸으로 이동한다.

if(nmg == 0){ //입력받은 홀수의 배수일 때 한칸 아래로 이동
            x ++;
}

 

 출력 결과는 아래 사진과 같다.

 

 여기까지가 조건을 충족하는 마방진의 코드였다. 동적 배열을 사용한 김에 덧붙이자면, 동적 배열에서 우리가 항상 잊지 말아야하는 것은 바로 배열의 해제다. 오늘 코드에서는 이 부분이 되겠다. 만약 동적 배열을 생성해놓고 해제를 하지 않으면 메모리 누수가 발생하기 때문에 항상 잊지 말고 해제를 해주도록 하자!

for(int i = 0; i < num; i++){
        delete[] arr[i];
    }
    delete[] arr;

 

 이렇게 해서 3주차 프로젝트 중 31게임과 마방진 생성에 대해 해설하는 시간을 마치도록 하겠다. 다음에는 클래스를 활용한 4칙연산 계산기 프로젝트에 대한 해설로 돌아오겠다. 그럼 이만

관련글 더보기