제1강. 난수와 시간 1
1. 난수 발생 만들기
난수: 정의된 범위 내에서 무작위로 추출된 수
난수의 필요성
- 게임은 항상 상대가 있어야 함. 여기서 상대라는 의미는 '적'이 될 수도 있고, '나 자신'이 될 수도 있고,
'어떤 목표'가 될 수도 있다.
- 컴퓨터 판단의 기본이 되기 때문임.
난수 생성 함수
1) rand() 함수
- srand()로 인해 생성된 값을 바탕으로 난수를 생성하는 함수
- 생성되는 난수의 범위를 지정하는 방법
- rand()는 기본값이 1이기 때문에 항상 난수테이블값이 1인 수가 생성된다.
-생성되는 난수의 범위를 지정하는 방법: ★ rand()% (마지막 값 - 시작값 + 1) + 시작 값
ex. 100~200 난수를 얻고 싶다면? rand()%101+100
#include<iostream>
using namespace std;
int main()
{
int randnum; //난수
for (int i = 0; i < 10; ++i)
{
randnum = rand();
cout << randnum << endl;
}
}

2) srand()함수
- 호출할 때 전달받는 인자를 기반으로 난수를 초기화하는 함수
- 정해진 순서가 안 나오도록 여러 개 만들어 매번 다른 난수표를 읽도록 만듦.
이 난수표를 선택하는 동작을 시드(Seed)라고 함.
- 매번 다른 값이 나오도록 하려면, 실행 시마다 다른 시드테이블 번호값이 나오게 해야함.
시간은 계속 바뀌므로 인자값에 time(NULL)을 넣어주면 해결.
=> ★ 기억할 것: srand((unsigend int)time NULL));
#include<iostream>
#include<ctime>
// ctime은 iostream 안에 있으므로 헤더를 안불러와도 됨
using namespace std;
int main()
{
srand((unsigned int)time(NULL));
int randnum; //난수
for (int i = 0; i < 10; ++i)
{
randnum = rand();
cout << randnum << endl;
}
}

예제 게임
< 1. 숫자 맞추기 게임(Up & down) >
- 게임 설명: 컴퓨터가 1에서 100까지의 수를 고른다. 사용자가 예상 수를 선택하면, 컴퓨터가 '크다', '작다',
'맞았다'를 알려 준다. 사용자가 선택한 수를 세어 몇 번만에 맞았는지를 알려 준다.
- 실행 예시

나의 코드:
#include<iostream>
using namespace std;
int main()
{
srand((unsigned int)time(NULL));
cout << "===================" << endl;
cout << "숫자 맞추기 게임" << endl;
cout << "===================" << endl;
cout << "설명: 1~100 숫자 중 하나를 알아내보세요." << endl;
cout << "종료는 0을 누르세요." << endl;
int inputNum;
int randNum = rand()%(100-1+1)+1;
int cnt = 0;
while (true)
{
cout << "숫자를 입력하세요: ";
cin >> inputNum;
cnt++;
//크다
if (inputNum == 0)
{
break;
}
else if (inputNum > randNum)
{
cout << inputNum << "보다 작은 수입니다." << endl;
}
//작다
else if (inputNum < randNum)
{
cout << inputNum << "보다 큰 수입니다." << endl;
}
else if(inputNum == randNum)
{
cout << "축하합니다. 당신은 " << cnt << "번만에 알아냈습니다." << endl;
break;
}
}
}
< 2. 무기 강화 게임 >
- 게임 설명: 무기 강화 레벨은 1, 강화 성공 확률은 50%로 시작한다.
- 플레이어가 할 수 있는 것은 1로 무기를 강화하거나, 2로 나가는 것이다.
- 무기 강화에 성공한다면 다음 강화 성공 확률이 1~5% 감소하고, 실패한다면 다음 강화는
성공 확률이 5~10% 증가한다. (단 1~5%와 5~10%는 랜덤하게 증감한다.)
- 2로 나간다면 게임이 종료된다.
- 실행 예시

나의 코드:
#include<iostream>
using namespace std;
int main()
{
srand((unsigned int)time(NULL));
int level = 1;
int successPercent = 50;
int randPercent;
int input;
//1로 무기를 강화하거나, 2로 나가기
//무기강화에 성공하면 1-5감소, 실패하면 확률 5-10증가(랜덤)
cout << "무기 강화 게임에 오신 것을 환영합니다!" << endl;
while (true)
{
cout << "현재 무기 강화 레벨: " << level << endl;
cout << "현재 강화 성공 확률: " << successPercent << "%" << endl;
cout << endl;
cout << "어떤 작업을 수행하시겠습니까?" << endl;
cout << "1. 무기 강화하기" << endl;
cout << "2. 나가기" << endl;
cout << "선택: ";
cin >> input;
if (input == 1)
{
//무기강화
randPercent = rand() % 101;
if (randPercent <= successPercent) // 성공
{
cout << "무기강화에 성공하였습니다." << endl;
successPercent -= rand() % 5 + 1;
level++;
}
else if (randPercent > successPercent) // 실패
{
cout << "무기강화에 실패하였습니다." << endl;
successPercent += rand() % 6 + 5;
}
}
else if (input == 2)
{
cout << "나가기를 선택하셨습니다. 게임을 종료합니다." << endl;
break;
}
cout << endl;
cout << "=========================" << endl;
}
}
< 3. 야구 게임 >
- 게임 설명: 1~9 사이의 랜덤한 숫자를 3개 얻어온다. 단, 숫자는 중복되어서는 안된다. 3개의 숫자는 * * *의
형태로 출력되고, 이 3개의 숫자를 맞추는 게임이다. 사용자는 이 3개의 숫자를 맞출 때까지 계속해서 3개씩
숫자를 입력한다.
만약, 맞춰야 할 숫자가 3 7 8일 경우 사용자는 3개의 숫자를 입력한다.
입력: 1 2 4를 입력했을 경우 1 2 4는 맞춰야 할 숫자 중 아무것도 없응므로 Out을 출력한다.
입력: 5 7 9를 입력했을 경우 7은 맞춰야 할 숫자 중 있고 위치도 같으므로 strike이다. 5 9는 없으므로 출력은
1strike 0ball을 출력한다.
입력: 3 8 6을 입력했을 경우 3은 1strike, 8은 숫자가 있지만 위치가 다르므로 1ball이므로 1strike 1ball이 출력된다.
이렇게 출력을 하고 입력을 받으면서 최종적으로 3개의 숫자를 자리까지 모두 일치하게 입력하면 게임이 종료된다.
※ 만약, 입력받은 숫자 3개 중 한 개 라도 0이 있을 경우 게임을 종료함.
※ 여기서 알고 갈 것!
\t: 특수 이스케이프 시퀀스로 탭 문자를 나타냄. 일정한 간격만큼 공백을 만들어줌.
- 실행 예시

나의 코드:
#include<iostream>
using namespace std;
int main()
{
srand((unsigned int)time(NULL));
cout << '*' << '\t' << '*' << '\t' << '*' << endl;
int arrnumber[9] = {};
for (int i = 0; i < 9; i++)
{
arrnumber[i] = i + 1;
}
//셔플 알고리즘
for (int i = 0; i < 100; ++i) //idx1과 idx2의 자리를 바꿔줌
{
// 배열 안에서 순서만 섞으므로 숫자가 중복될 이유 x
int idx1, idx2, temp;
idx1 = rand() % 9 ; // 배열의 인덱스는 0~8이므로
idx2 = rand() % 9 ;
temp = arrnumber[idx1]; // arrnumber[3]의 값 임시저장
arrnumber[idx1] = arrnumber[idx2]; //arrnumber[3]에 arrnumber[2] 저장
arrnumber[idx2] = temp; //arrnumber[2]에 arrnubmer[3]저장
}
int gamecnt = 0; int inputNum[3];
while (true)
{
gamecnt++;
int strike=0, ball=0; //리셋
cout << gamecnt << "회" << endl;
cout << "1~9 사이의 숫자 중 3개를 입력하세요(0: 종료): ";
cin >> inputNum[0] >> inputNum[1] >> inputNum[2];
if (inputNum[0] == 0 || inputNum[1] == 0 || inputNum[2] == 0)
{
cout << "게임을 종료합니다." << endl;
break;
}
else
{
for (int i = 0; i < 3; ++i)
{ //strike
if (arrnumber[i] == inputNum[i])
{
strike++;
}
for (int j = 0; j < 3; ++j)
{
if (arrnumber[i] == inputNum[j])
{
if (i == j)
continue;
ball++;
}
}
}
if (strike == 0 && ball == 0) //out
{
cout << "Out!" << endl;
}
else if (strike == 3)
{
cout << "축하드려요 ~!! 숫자를 전부 맞추었습니다!!" << endl; //몇회만에 맞추었는지
break;
}
else
{
cout << strike << " strike " << ball << " ball" << endl;
}
}
}
}
-2Player ver.

※ 여기서 알고 갈 것!
- 계속하려면 아무 키나 누르십시오...는 system("pause")를 활용하세요!
- 화면을 지우는 함수는 system("cls")입니다.
#include<iostream>
using namespace std;
int main()
{
int answerNum1[3];
int answerNum2[3];
cout << "Player1의 차례입니다." << endl;
cout << "Player2가 맞출 숫자를 입력하세요: ";
for (int i = 0; i < 3; ++i)
{
cin >> answerNum2[i];
}
system("cls");
cout << "Player2의 차례입니다." << endl;
cout << "Player1이 맞출 숫자를 입력하세요: ";
for (int i = 0; i < 3; ++i)
{
cin >> answerNum1[i];
}
system("cls");
int strike, ball, gamecnt = 1;
int arrInput[3]; //공통
while (true)
{
cout << '*' << '\t' << '*' << '\t' << '*' << endl;
cout << gamecnt << "회" << endl;
cout << "Player1의 차례입니다" << endl;
cout << endl;
cout << "1 ~ 9 사이의 숫자 중 3개를 입력하세요(0: 종료) : ";
cin >> arrInput[0] >> arrInput[1] >> arrInput[2];
// 0을 입력하면 종료.
if (arrInput[0] == 0 || arrInput[1] == 0 || arrInput[2] == 0)
{
break;
}
ball = 0, strike = 0;
for (int i = 0; i < 3; ++i)
{
if (answerNum1[i] == arrInput[i])
strike++;
for (int j = 0; j < 3; ++j)
{
if (i == j) // 스트라이크는 이미 처리했으므로
continue;
if (answerNum1[i] == arrInput[j])
ball++;
}
}
if (strike == 0 && ball == 0)
{
cout << "Out" << endl;
}
else if (strike == 3)
{
cout << "정답입니다!!! 1플레이어의 승리입니다. 게임을 종료합니다.";
break;
}
else
{
cout << strike << "stirke " << ball << "ball" << endl;
}
system("pause");
system("cls");
cout << '*' << '\t' << '*' << '\t' << '*' << endl;
cout << gamecnt << "회" << endl;
cout << "Player2의 차례입니다" << endl;
cout << endl;
cout << "1 ~ 9 사이의 숫자 중 3개를 입력하세요(0: 종료) : ";
cin >> arrInput[0] >> arrInput[1] >> arrInput[2];
// 0을 입력하면 종료.
if (arrInput[0] == 0 || arrInput[1] == 0 || arrInput[2] == 0)
{
break;
}
ball = 0, strike = 0;
for (int i = 0; i < 3; ++i)
{
if (answerNum2[i] == arrInput[i])
strike++;
for (int j = 0; j < 3; ++j)
{
if (i == j) // 스트라이크는 이미 처리했으므로
continue;
if (answerNum2[i] == arrInput[j])
ball++;
}
}
if (strike == 0 && ball == 0)
{
cout << "Out" << endl;
}
else if (strike == 3)
{
cout << "정답입니다!!! 2플레이어의 승리입니다. 게임을 종료합니다.";
break;
}
else
{
cout << strike << "stirke " << ball << "ball" << endl;
}
system("pause");
system("cls");
gamecnt++;
}
}
입력 예외 처리
cin.fail()
: 입력 에러가 발생하면 true
cin.clear()
: cin 객체 내부 상태 플래그 초기화, 플래그만을 건드리고 버퍼에는 조작을 가하지 않음.
cin.ignore(무시문자 최대길이, 종료문자)
: 현재 버퍼 안에 있는 내용 무시, 종료 문자가 나오면 끝냄. 또는 무시문자 최대 길이 후 끝.
이걸로 버퍼를 비워도 내부상태가 fail상태면 더 이상 입력작업은 실패함.
=> ignore로 현재버퍼 무시하고, clear로 상태 플래그 초기화해야함.
- 예를 들어, cin.ignore(n, delimiter)는 입력 버퍼에서 최대 n개의 문자를 delimiter 문자가 나올 때까지 제거함.
만약 delimiter를 생략하면 기본적으로 개행 문자('\n')가 delimiter로 사용된다.
- 예를 들어, cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');와 같이 사용하여 버퍼를 비워줄 수 있다.
#include<iostream>
#include<limits>
using namespace std;
int main()
{
int a;
while (true)
{
cin >> a;
if (cin.fail())
{
cout << "비정상 입력. " << endl;
cin.clear();
cin.ignore(1024, '\n');
//cin.ignore(numeric_limits<>)
}
else
{
cout << a << endl;
}
}
}
시간 관련 개념 ( <time.h>헤더)
1) time() 함수
- time(NULL): 1970년 1월 1일 0시(TimeZone: UTC)이후부터 인자값까지 현재 흐른 초 수를 리턴해줌.
- srand 함수의 인자로 time(NULL)을 넘긴 후 rand 함수를 호출하면, 시간을 기준으로 초기화되는 난수를 생성할 수 있어
프로그램을 실행할 때마다 다른 난수 값을 얻을 수 있음.
2) localtime_s() 함수
- time 함수 결과로 나온 초 값을 tm구조체 형식으로 리턴해줌. localtime_s(tm 구조체 타입 변수의 포인터,
time_t 타입 변수의 포인터)
tm구조체 변수에 time_t 타입 변수의 정보를 저장함.
3) time_t 타입
- time 헤더에서 시간을 잘 다루기 위해서 만들어진 데이터 타입으로, 1970년 1월 1일 00시 00분부터 지금까지
초단위의 시간을 정수값으로 표현하는 데이터
4) struct tm 구조체

※ 현재 시간 활용 예시
#include<iostream>
#include<time.h>
using namespace std;
int main()
{
time_t timer;
struct tm t;
timer = time(NULL); //현재시간
localtime_s(&t, &timer); // tm구조체 변수, time_t 타입 변수
cout << t.tm_year + 1900 << "년 " << t.tm_mon + 1 << "월 " << // 1900년 이후부터 측정되기 때문에 +1900
t.tm_mday << "일 " << t.tm_hour << "시 " << t.tm_min << "분 " << t.tm_sec << "초 ";
if (t.tm_wday == 0) cout << "일요일"; //일요일 0~ 6 토요일
else if (t.tm_wday == 1) cout << "월요일";
else if (t.tm_wday == 2) cout << "화요일";
else if (t.tm_wday == 3)cout << "수요일";
else if (t.tm_wday == 4)cout << "목요일";
else if (t.tm_wday == 5)cout << "금요일";
else cout << "토요일";
}
예제 게임 2
<4. 숫자 기억 게임>
- 게임 설명:
1. 1~ 100까지 랜덤한 숫자를 저장한다. (중복상관 없음.)
2. easy, noraml, hard 중 게임 모드를 선택한다.
3. 게임 난이도에 따라서 다르게 숫자를 보여준다.
- easy모드: 5개의 숫자를 1초 간격으로 보여줌.
- normal모드: 10개의 숫자를 0.7초 간격으로 보여줌.
- hard모드: 15개의 숫자를 0.5초 간격으로 보여줌.
4. 화면을 모두 지운다.
5. 플레이어가 아까 보여준 숫자를 순서대로 입력한다.
6. 중간에 숫자가 틀리면 "땡! 틀렸습니다." 를 출력하고 종료
※ 활용되는 함수
- Sleep(ms): 프로그램에서 일정 시간 동안 작업을 대기(wait)할 때 사용.
=> 추가 헤더파일: #include<Windows.h>
ex.Sleep(1000): 1초 대기

- 실행 예시

#include <iostream>
#include <Windows.h>
using namespace std;
enum class MODE
{
EASY =1, NORMAL,HARD
};
void Init();
int UserInput(); //유저입력
void GameStart(MODE mode, int sleeptime); // 모드,대기시간
int main()
{
Init();
int select = UserInput();
system("cls");
switch (select)
{
case(int)MODE::EASY: //1이면
GameStart(MODE::EASY, 1000);
break;
case(int)MODE::NORMAL: //2
GameStart(MODE::NORMAL, 700);
break;
case(int)MODE::HARD: //3
GameStart(MODE::HARD, 500);
break;
}
}
void Init() //시작 문구
{
cout << "=======================================" << endl;
cout << "숫자 기억 게임입니다. 모드를 선택하세요." << endl;
cout << "EASY: 1, NORMAL: 2, HARD: 3" << endl;
cout << "=======================================" << endl;
srand((unsigned int)time(NULL));
}
int UserInput() //예외처리&입력
{
int select;
while (true)
{
cin >> select;
if (cin.fail() || select < 1 || select > 3)
{
cin.clear();
cin.ignore(1024, '\n');
cout << "잘못된 입력입니다. EASY:1, NORMAL:2, HARD:3 중에 선택하세요." << endl;
}
else
return select;
}
}
void GameStart(MODE mode, int sleeptime)
{
int modenum; //숫자 개수
//EASY, NORMAL, HARD 텍스트 안내 출력
switch (mode)
{
case MODE::EASY: //MODE:: ? 배웠던거같은데... enumcalss?
modenum = 5;
cout << "Easy 모드입니다." << endl;
break;
case MODE::NORMAL:
modenum = 10;
cout << "Normal 모드입니다." << endl;
break;
case MODE::HARD:
modenum = 15;
cout << "Hard 모드입니다." << endl;
break;
}
int* pNumber = new int[modenum]; // 숫자개수만큼의 배열 크기를 생성하고 그걸 포인터
//숫자가 출력됌. 1~ 100
for (int i = 0; i < modenum; ++i) //숫자개수만큼 반복
{
pNumber[i] = rand() % 100 + 1;
cout << pNumber[i] << " ";
Sleep(sleeptime); // 1초 대기
}
system("cls");
cout << "방금 본 숫자를 입력하세요 : " << endl;
//입력 받기
int input;
for (int i = 0; i < modenum; ++i)
{
cin >> input;
if (pNumber[i] != input)
{
cout << "땡! 틀렸습니다." << endl;
delete[] pNumber; //메모리 해제
return;
}
}
cout << "축하합니다. 모두 맞추셨네요~!" << endl;
delete[] pNumber; //메모리 해제
}
<5. 시간 맞히기 게임>
- 게임 설명
1. 실행하면 키를 입력받는다.
2. 입력 후 플레이어는 10초를 세고 키를 누른다.
3. 10초보다 빨리 다시 입력했을 경우 "당신이 졌습니다. 시간이 지나지 않았습니다." 를 출력한다.
4. 10초를 맞추어쓸 경우 "축하합니다. 당신은 감각이 엄청나시군요?"를 출력한다.
5. 10초보다 느리게 입력을 했을 경우 "당신이 졌습니다. 시간이 지났습니다."를 출력한다.
키보드 처리 관련 함수들
<conio.h> : 콘솔(console) 입출력(input output) 함수를 제공하는 헤더파일
1) _getch()함수:
- '표준 입력'(사용자 입력하고 enter를 눌러야 반응하는 것, ex. scanf, cin- 버퍼 사용)과 달리 '콘솔 입력'을
사용하기 때문에 입력된 키값을 즉시 반환하는 함수.(버퍼를 사용하지 않음)
※ getch()라는 POSIX 함수 이름을 쓰면 아래와 같은 오류가 나오니 ISO C++ 호환 이름인 _getch()를 쓰자.

2) _kbhit()함수: 사용자가 키보드의 입력 여부를 단순히 입력 버퍼만 확인하고 true, false값을 리턴하는 함수
- 실행 예시


※ 여기서 알고 갈 것!
- \n(Line Feed): 다음 행으로
- \r(carriage Return): 커서를 행의 앞으로
첫번째 시도:
#include <iostream>
#include <Windows.h>
#include <conio.h>
using namespace std;
void Init();
int main()
{
Init();
while (true)
{
if (_kbhit())
break;
}
for (int i = 0; i < 3; ++i)
{
cout << '\r' << 3 - i << "...";
Sleep(1000);
}
cout << '\r' << "!!!!시작!!!!" << endl;
// 첫 번째 키 입력 시간 기록
tie_t start_time = time(NULL);
// 키 입력 기다리기
while (true)
{
if (_kbhit())
{
cin.clear();
break;
}
}
// 두 번째 키 입력 시간 기록
time_t end_time = time(NULL);
// 경과 시간 계산 (밀리초 단위)
time_t elapsed_time = end_time - start_time;
// 결과 출력
if (elapsed_time < 10000)
cout << "당신이 졌습니다. 시간이 지나지 않았습니다." << endl;
else if (elapsed_time == 10000)
cout << "축하합니다. 당신은 감각이 엄청나시군요?" << endl;
else
cout << "당신이 졌습니다. 시간이 지났습니다." << endl;
return 0;
}
void Init()
{
cout << "====================" << endl;
cout << '|' << " " << "시간맞히기게임" << " " << '|' << endl;
cout << "====================" << endl;
cout << "--------------------------------------------------" << endl;
cout << "설명: 시간을 재는 게임입니다." << endl;
cout << "처음 key와 나중에 누른 key 사이의 시간을 10초로 만들어놓았습니다." << endl;
cout << "==================================================" << endl;
cout << "아무키나 누르세요." << endl;
cout << "--------------------------------------------------" << endl;
}
⚠️오류: 처음 키를 입력할때 3초 카운트 뒤 처음시간을 기록하고
그 다음 키 입력이 있을 때까지 다음 코드를 실행을 하지 않고 싶은데 while문을 무시하고
결과를 출력해버렸다.
별짓을 다해보고 난리를 떨어도 실행하면 고쳐지지않았다. 그래서 만능 GPT에게 물어보았다.


결론: _kbhit()함수는 키 입력 버퍼를 비워주지 않기 때문에, 무한루프에서 _kbhit()함수가 계속해서 참을 반환하게 된다. 그래서 while문을 빠져나온다.
코드수정:
#include <iostream>
#include <Windows.h>
#include <conio.h>
using namespace std;
void Init();
int main()
{
Init();
while (true)
{
if (_kbhit())
{
// 키 입력이 감지되면 키 값을 가져와서 사용자가 입력한 키를 처리
char key = _getch(); // 버퍼 비워주기?
break;
}
}
for (int i = 0; i < 3; ++i)
{
cout << '\r' << 3 - i << "...";
Sleep(1000);
}
cout << '\r' << "!!!!시작!!!!" << endl;
// 첫 번째 키 입력 시간 기록
time_t start_time = time(NULL);
// 키 입력 기다리기
while (true)
{
if (_kbhit())
{
// 키 입력이 감지되면 키 값을 가져와서 사용자가 입력한 키를 처리
char key = _getch();
//cin.clear(); // cin 버퍼 비움
break;
}
}
// 두 번째 키 입력 시간 기록
time_t end_time = time(NULL);
// 경과 시간 계산 (밀리초 단위)
time_t elapsed_time = end_time - start_time;
// 결과 출력
if (elapsed_time < 10)
cout << "당신이 졌습니다. 시간이 지나지 않았습니다." << endl;
else if (elapsed_time == 10)
cout << "축하합니다. 당신은 감각이 엄청나시군요?" << endl;
else
cout << "당신이 졌습니다. 시간이 지났습니다." << endl;
return 0;
}
void Init()
{
cout << "====================" << endl;
cout << '|' << " " << "시간맞히기게임" << " " << '|' << endl;
cout << "====================" << endl;
cout << "--------------------------------------------------" << endl;
cout << "설명: 시간을 재는 게임입니다." << endl;
cout << "처음 key와 나중에 누른 key 사이의 시간을 10초로 만들어놓았습니다." << endl;
cout << "==================================================" << endl;
cout << "아무키나 누르세요." << endl;
cout << "--------------------------------------------------" << endl;
}