3강 - 도형그리기 및 마우스

PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps); 
  • BeginPain는 HDC를 반환하고 H가 맨 앞에 있는걸로 보아 handle을 의미한다. dc는 클라이언트 화면에 그리기(출력하기) 위한 도구이다.

  • dc를 사용해 문자를 출력해보자.

샘플 코드

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps); 
            // TODO: Add any drawing code that uses hdc here...

            /* 마우스의 위치 출력*/
            TCHAR mouse[64] = {};

            // wsprintf : 유니코드 문자열을 만들어주는 함수
            wsprintf(mouse, TEXT("x : %d y : %d"), _area.start.x, _area.start.y);

            // lstrlen(변수) : 유니코드 문자열의 길이를 구하는 함수
            TextOut(hdc, 600, 30, mouse, lstrlen(mouse));

            /* 텍스트 출력 */
            // 핸들, 좌표x, 좌표y, 유니코드문자열, 글자 갯수
            // 유니코드 문자열을 작성할 때, 따옴표 앞에 L을 붙이거나 TEXT 매크로를 이용한다.
            // L을 쓰는 것 보다 TEXT를 사용하는 것이 좋다. 왜냐하면 TEXT가 알아서
            // 멀티바이트인 경우에도 처리해주기 때문
            TextOut(hdc, 50, 50, TEXT("win32"), 5);
            TextOut(hdc, 55, 55, TEXT("win32"), 5);

            /* 도형 출력 */

            /* 사각형 그리기 */
            // 핸들, 사각형 왼쪽 상단 좌표x,y , 사격형 오른쪽 하단 좌표x,y, 
            Rectangle(hdc, 45, 45, 200, 200);            

            // ! 사각형을 텍스트 위에 출력하면 텍스트를 위로 덮어서 텍스트가 보이지 않는다.
            TextOut(hdc, 60, 60, TEXT("win32"), 5);

            /* 선 그리기 */
            // 시작점과 끝점을 설정
            // 선의 시작점 설정
            MoveToEx(hdc, 70, 100, NULL);
            // 끝점
            LineTo(hdc, 400, 150);
            // 끝점을 한번더 호출하면 끝점이 시작점이 되어 연결가능
            LineTo(hdc, 500, 100);

            // 시작점을 다시 지정해서 새로 그리기 가능
            MoveToEx(hdc, 600, 300, NULL);
            LineTo(hdc, 700, 400);

            /* 원 */
            // 45,45,200,200 사각형에 딱 맞는 원이 생성된다.
            Ellipse(hdc, 45, 45, 200, 200);

            /* 마우스 드래그하면 사각형이 그려지는 기능을 만들어보자 */
            if (_area.isStarted)
            {
                Rectangle(hdc, _area.start.x, _area.start.y, _area.end.x, _area.end.y);    
            }

            EndPaint(hWnd, &ps);
        }
        break;

        // 키가 눌러졌을때 들어오는 메세지

        // 마우스 좌클릭
    case WM_MOUSEMOVE:
        // 마우스가 움직일 때 들어오는 메시지
        if (_area.isStarted)
        {
            _area.end.x = lParam & 0x0000ffff;
            _area.end.y = lParam >> 16;

            InvalidateRect(hWnd, NULL, TRUE);
        }
        break;
    case WM_LBUTTONUP:
        // 마우스 왼쪽버튼이 떼질 때 발생하는 메시지
        if (_area.isStarted)
        {
            _area.isStarted = false;
            _area.end.x = lParam & 0x0000ffff;
            _area.end.y = lParam >> 16;

            InvalidateRect(hWnd, NULL, TRUE);
        }

        break;
    case WM_LBUTTONDOWN:
        // 마우스 위치는 lParam에 들어오게 되는데 16비트로 쪼개서 x,y값이
        // 32비트에 들어오게 된다. LOWORD, HIWORD 매크로를 이용해서 하위, 상위 16비트 값을 얻어온다.
        if (!_area.isStarted)
        {
            _area.isStarted = true;

            // 만약 lParam이 0x12345678 >> 16일때 결과는? 1234
            _area.start.x = lParam & 0x0000ffff;
            _area.start.y = lParam >> 16;

            _area.end.x = _area.start.x;
            _area.end.y = _area.start.y;
            // InvalidateRect 함수는 강제로 WM_PAINT 메시지를
            // 호출해주는 함수... 1번 인자 : 윈도우 핸들, 2번 인자 : 초기화할 영역
            // NULL을 넣어줄 경우 전체화면을 대상으로 갱신 진행
            // 3번 인자 : TRUE일 경우 현재 화면을 지우고 갱신, FALSE일 경우 안지우고 갱신
            InvalidateRect(hWnd, NULL, TRUE);
        }

        break;

    case WM_KEYDOWN:
        // 이 메시지가 들어올 경우 wParam에 어떤 키를 눌렀는지가 들어온다.
        switch (wParam)
        {
        case VK_ESCAPE:
            DestroyWindow(hWnd);
            break;
        }
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

마우스 드래그 사각형 정리

처음에 구조체를 생성
struct _tagArea
{ bool isStarted;
POINT start;
POINT end;
};

마우스 움직이면

  1. 현재 마우스의 위치가 끝점이다.
  2. 즉, 움직일때 계속 위치가 갱신되어서 드래그 사각형이 계속 생성된다.

누르면

  1. bool 변수에 true를 넣는다.
  2. 시작점 저장
  3. 끝점을 초기화 해준다.

떼면

  1. bool 변수에 false를 넣는다.
  2. 끝점 저장

1강 - 기본 윈도우창 Part.1

API란? = 함수 집합. 굉장히 많은 windows의 기능들을 이용하기 위한 함수 집합

Windows API 는 C와 C++ 언어를 기반으로, 함수와 구조체를 기반으로 기능을 제공

방대한 기능을 모두 외우고 사용할 수는 없다. 따라서 필요한 기능들을 선택적으로 공부해야한다.

중요한 것은 C++ 프로그래밍 능력이다.

1. 프로젝트 생성 방법

'Win32 콘솔 응용 프로그램'이 아닌 'Win32 프로젝트'로 프로젝트 생성

프로젝트를 생성하면 미리 컴파일된 파일(precompiled file)들이 많이 있다.

stadfx.h 라는 헤더는 한번 컴파일한 후, 수정이 되기 전까지 컴파일이 실행되지 않음. 따라서 성능향상에 도움이 된다.

(프로젝트 이름).cpp 에 미리 생성된 코드들이 windows에 프로그램을 만들기 위해 미리 생성 된 코드이다.

F5로 실행하게 되면 콘솔이 아닌 실제 Windows에서 동작되는 창이 뜬다.

그렇다면 어떻게 이것이 가능한가?

windows.h에 기닝들이 포함되어 있으므로 반드시 include되어야한다.

기존에 했던 코딩의 진입점은 int main()이다. 그러나 windows에서는

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)

여기에 있는 여럿 인자들을 살펴보자.

우선, APIENTRY은 define 되어있는 걸 볼 수 있는데

#define WINAPI    __stdcall
#define APIENTRY  WINPAI

__stdcall = 함수 호출 규약
(함수 호출하는 방법에 대해서 여러 방법이 있는데 그 중 하나임을 명사하는 것이다.)

인자의 종류 중 HINSTANCE hInstance를 살펴 보자

HINSTANCE

  • windows에서 앞에 h가 붙어있으면 handle을 의미한다. handle이란 가는 방향을 조종한다... instace는 class를 기준으로 설멍하자면 class를 만든다고 동작되는 것이 아니고 class를 변수로 선언하고 초기값을 설정한 후 사용가능하다. class 형태를 제공하는 것이 interface, 제공되는 class를 사용해서 실제 사용하는 객체를 Instace라고 한다.
  • 운영체제는 운영체제 안에 있는 모든 프로그램들을 관리해야 한다. 그리고 프로그램들이 어떤 프로그램들인지 구분해야 한다. 그러기 위해서는 프로그램마다 그 프로그램이 무엇인지 식별번호를 저장하는 변수가 필요하다. 이것이 HINSTANCE이다.
  • exe 파일을 실행하면 exe의 코드가 메모리의 코드영역에 올라간다. 그러면 CPU가 그 코드영역의 코드를 동작시킨다. 이 때 운영체제는 이 프로그램에 식별번호(HINSTANCE)를 부여한다. 결과적으로 우리가 수정하는 영역이 아니다.
  • 커널이라는 개념... 커널도 운영체제가 사용하는 기능이다. 커널에서 사용되는 오브젝트를 커널 오브젝트라고 한다. handle 종류가 커널 오브젝트에 속한다.

LoadString

  • 실행하면 타이틀 바에 제목이 있다. 이 타이틀 바의 제목을 문자열로 지정가능하다. 내부적으로 string table에 이 제목 변수를 넣어놓았다. LoadString이 그 string table을 읽어주는 방식이다. map 컨테이너처럼 key와 value로 구성되어 있다.
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
  • 여기에서 IDS_APP_TITLE이 id(key)이고 szTitle은 위에 전역변수로 선언되어 있는데,
> WCHAR szTitle[MAX_LOADSTRING];
  • 이렇게 선언되어 있거 WCHAR을 F12번을 눌러 들어가보자.
typedef wchar_t WCHAR;
  • 를 발견할 수 있다. 주석에는 16-bit UNICODE character라고 되어 있는데 다양한 언어를 지원하기 위해 나온게 2byte UNICODE이다.

  • 이걸 바꾸기 위해서는 '프로젝트 이름'에 우클릭 하고 '속성'에 간 다음 '일반'에 '문자 집합' 부분을 '멀티바이트 문자 집합'으로 바꿔주면 된다.

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;
  • WNDCLASSEXW는 구조체이다. 운영체제에는 registry가 존재하는데, 실행되는 프로그램에 대한 key값이 저장되어 있다. 실행 전에 registry를 저장하는 함수가 MyRegisterClass이다.

WndProc

wcex.lpfnWndProc    = WndProc;        
  • 중요!!! : 메시지 기반 운영체제가 Windows... 즉 마우스, 키보드 등의 입력을 이벤트라고 부른다. 또는 화면보호기를 띄워라 등. 이런 이벤트가 발생하면 어떤 이벤트냐에 따라서 이것을 메시지로 만들어 준다. 만들어 준 메시지를 내부에 존재하는 메시지 큐에 저장한다. 즉 이벤트가 발생한 순서대로 꺼내서 처리한다. 이벤트가 발생하는 것을 while로 계속 감시하고 이것을 메시지 루프라고 한다.

  • 메시지에 따른 사용자 정의 기능을 만들어 주어야한다. 즉 큐에 어떤 메시지가 들어있는지 정확히 알아야한다. 이 기능을 하는 것이 바로 WndProc이다.

  • WndProc이라는 함수 이름은 즉 함수 포인터와 같은 의미이다. 함수포인터를 왜 전달했을까? 포인터를 사용하는 이유는 '주소에 접근해서 값을 변경하기 위해서'이다. 따라서 그 주소에 있는 함수에 접근해서 실행하라는 뜻이다. Windows운영체제는 사용자가 만든 함수를 모른다. 그래서 함수의 포인터를 전달해서 실행시킨다. 즉 사용자 정의 함수를 만들고 사용하기 위해서이다.

hIcon, hIconSm

  • 윈도우에서 사용하는 아이콘 이미지이고 sm이 들어가면 윈도우 bar에 들어가는 작은 아이콘 이미지이다.

hCursor

  • 마우스 커서의 모양이다.

hbrBackground

  • 화면 배경 색

lpszMenuName

  • 리소스에 이미 만들어져 있다. 윈도우 바 밑에 있는 탭다운 메뉴이다. 그러나 사용되지 않을 예정이다.

lpszClassName

  • 레지스터에 등록할 클래스 이름.

날짜 : 190524

주제 : 파일 입출력, 클래스를 활용하여 마리오 게임을 만들어본다.

특징 : 아직 스테이지 구현은 하지 않았다. STL을 아직 쓰지 않고 배열로만 만들었다. 클래스를 사용하다 보니 코드가 분산되어 여기에 직접 올리지 못했다.

 

GitHub : https://github.com/crobbit-kent/CPP_BASIC_PRACTICE/tree/master/Mario

날짜 : 190523

주제 : 텍스트 파일에 미로를 직접 그리고 파일을 불러와서 미로 게임을 만들어 본다.

특징 : 파일 저장과 로딩을 텍스트 파일 또는 바이너리 파일로 할 수 있다.

 

#include <iostream>

using namespace std;

#define NAME_SIZE 32
#define STUDENT_MAX 3

typedef struct _tagStudent
{
	char name[NAME_SIZE];
	int number;
	int kor;
	int eng;
	int math;
	int total;
	int average;
}STUDENT, *PSTUDENT;

int main()
{
	/*
	STUDENT student[STUDENT_MAX] = {};

	for (int i = 0; i < STUDENT_MAX; ++i)
	{
		cin.clear();
		cin.ignore(1024, '\n');
		cout << "이름 : ";
		cin.getline(student[i].name, NAME_SIZE - 1);

		student[i].number = i + 1;

		cout << "국어 : ";
		cin >> student[i].kor;

		cout << "영어 : ";
		cin >> student[i].eng;

		cout << "수학 : ";
		cin >> student[i].math;

		student[i].total = student[i].math + student[i].eng + student[i].kor;
		student[i].average = student[i].total / 3.f;
	}

	// 학생 정보를 파일에 저장
	int studentCount = STUDENT_MAX;

	FILE* file = NULL;

	fopen_s(&file, "Student.std", "wb");

	if (file)
	{
		// 학생 수를 저장
		fwrite(&studentCount, 4, 1, file);

		for (int i = 0; i < studentCount; ++i)
		{
			// 학생 구조체를 통으로 저장
			fwrite(&student[i], sizeof(STUDENT), 1, file);
		}

		fclose(file);
	}
	*/

	STUDENT student[STUDENT_MAX] = {};

	FILE* file = NULL;


	fopen_s(&file, "Student.std", "rb");

	int studentCount = 0;

	if (file)
	{


		// 학생수를 먼저 읽어 온다.
		fread(&studentCount, 4, 1, file);

		for (int i = 0; i < studentCount; ++i)
		{
			fread(&student[i], sizeof(STUDENT), 1, file);
		}

		fclose(file);
	}

	for (int i = 0; i < studentCount; ++i)
	{
		cout << "이름 : " << student[i].name << endl;
		cout << "국어 : " << student[i].kor << endl;
		cout << "영어 : " << student[i].eng << endl;
		cout << "수학 : " << student[i].math << endl;
		cout << "총점 : " << student[i].total << endl;
		cout << "평균 : " << student[i].average << endl << endl;
	}

	system("pause");

	return 0;
}

 

날짜 : 190521

주제 : 더블 링크드 리스트 구현

설명 : 싱글과 다르게 더블은 노드 구조체 안에 앞에 있는 노드와 뒤에 있는 노드를 모두 저장하고 있다. 따라서 노드를 뒤에서부터 순회할 수 있다.

 

#include <iostream>

using namespace std;

#define NAME_SIZE 32

/* 링크드 리스트 : 자료구조의 한 종류. 자료구조란 데이터를 저장하는 방법
링크드 리스트는 데이터 목록을 연결시켜서 접근할 수 있는 구조를 제공

특징 : 선형구조... 즉 배열처럼 특정요소에 바로 접근 불가. 무조건 앞에서부터 차례대로 들어가야한다.
노드를 추가할때 노드를 생성하고 마지막 노드에 연결. 따라서 개수의 제한이 없음.


노드 : 데이터를 저장하기 위한 공간
노드의 특징 : 다음노드에 연결가능(주소를 저장)
*/

/*
정렬 기능 추가... 기준은 학번 또는 평균
*/

enum MAIN_MENU
{
	MM_NONE,
	MM_INSERT,
	MM_DELETE,
	MM_SEARCH,
	MM_PRINT,
	MM_SORT,
	MM_EXIT
};

enum PRINT_MENU
{
	PM_NONE,
	PM_FORWARDS,
	PM_BACKWARDS,
	PM_BACK
};

enum SORT_MENU
{
	SM_NONE,
	SM_NUMBER,
	SM_AVERAGE,
	SM_BACK
};

// 학생 정보 구조체
typedef struct _tagStudent
{
	char	name[NAME_SIZE];
	int		number;
	int		kor;
	int		eng;
	int		math;
	int		total;
	float	average;
}STUDENT, *PSTUDENT;

// 노드 구조체
typedef struct _tagNode
{
	STUDENT studentData;
	_tagNode* nextNode;
	_tagNode* prevNode;
}NODE, *PNODE;

// 리스트 구조체
typedef struct _tagList
{
	PNODE	begin;
	PNODE	end;
	int		currentSize;
	int		studentNumberStandard;
}LIST, *PLIST;

void InitList(PLIST list)
{
	// 포인터 초기화 방식
	list->begin = NULL;
	list->end = NULL;
	list->currentSize = 0;
}

int InputInt()
{
	int input;
	cin >> input;

	if (cin.fail())
	{
		cin.clear();
		cin.ignore(1024, '\n');
		return INT_MAX;
	}

	return input;
}

int PrintMenu()
{
	system("cls");
	cout << "1. 학생 추가" << endl;
	cout << "2. 학생 삭제" << endl;
	cout << "3. 학생 탐색" << endl;
	cout << "4. 학생 출력" << endl;
	cout << "5. 학생 정렬" << endl;
	cout << "6. 종료" << endl << endl;

	cout << "메뉴를 선택하세요 : " << endl;

	int input = InputInt();

	if (input <= MM_NONE || input > MM_EXIT)
	{
		return MM_NONE;
	}

	return input;
}

void InputString(char* string, int size)
{
	cin.clear();
	cin.ignore(1024, '\n');

	cin.getline(string, size - 1);
}

void Insert(PLIST list)
{
	system("cls");
	cout << "========== 1. 학생 추가 ==========" << endl;

	STUDENT student = {};

	cout << "이름 : ";
	InputString(student.name, NAME_SIZE);

	cout << "국어 점수 : ";
	student.kor = InputInt();

	cout << "영어 점수 : ";
	student.eng = InputInt();

	cout << "수학 점수 : ";
	student.math = InputInt();

	student.total = student.kor + student.eng + student.math;
	student.average = student.total / 3.f;

	++list->studentNumberStandard;
	++list->currentSize;
	student.number = list->studentNumberStandard;


	// 추가할 리스트 노드를 생성
	PNODE	node = new NODE;

	// 마지막에 추가되는 노드이기 때문에 그 뒤에 노드는 없음
	node->nextNode = NULL;

	// 입력받은 데이터를 노드에 저장
	node->studentData = student;

	// 헤드노드가 비어있으면 새로 생성된 노드가 헤드노드
	if (list->begin == NULL)
	{
		list->begin = node;
		node->prevNode = NULL;
	}
	else
	{
		node->prevNode = list->end;
		list->end->nextNode = node;
	}

	// 새로 생성된 노드가 마지막 노드
	list->end = node;
}

//void InsertFront(PLIST list)


void ClearList(PLIST list)
{
	PNODE	node = list->begin;

	while (node != NULL)
	{
		// 다음 노드 저장
		PNODE	next = node->nextNode;

		// 현재 노드 삭제
		delete node;

		// 이제 다음 노드로
		node = next;
	}

	// 마지막으로 모두 초기화
	list->begin = NULL;
	list->end = NULL;
	list->currentSize = 0;
}

const void PrintStudentData(const PSTUDENT student)
{
	cout << "이름 : " << student->name << "\t학번 : " << student->number << endl;
	cout << "국어 점수 : " << student->kor << "\t영어 점수 : " << student->eng << endl;
	cout << "수학 점수 : " << student->math << endl;
	cout << "총 점 : " << student->total << "\t평 균 : " << student->average << endl << endl;
}

const void PrintListBackwards(const PLIST list)
{
	PNODE	node = list->end;

	while (node != NULL)
	{
		PrintStudentData(&node->studentData);
		node = node->prevNode;
	}

	cout << "총 학생 수 : " << list->currentSize << endl;
}

const void PrintListForwards(const PLIST list)
{
	PNODE	node = list->begin;

	while (node != NULL)
	{
		PrintStudentData(&node->studentData);
		node = node->nextNode;
	}

	cout << "총 학생 수 : " << list->currentSize << endl;
}

const void PrintList(const PLIST list)
{
	while (true)
	{
		system("cls");
		cout << "========== 4. 학생 정보 출력 ==========" << endl;
		cout << "1. 정방향 출력" << endl;
		cout << "2. 역방향 출력" << endl;
		cout << "3. 뒤로 가기" << endl;
		int menu = InputInt();

		switch (menu)
		{
		case PM_FORWARDS:
			PrintListForwards(list);
			break;
		case PM_BACKWARDS:
			PrintListBackwards(list);
			break;
		case PM_BACK:
			return;
			break;
		}

		system("pause");
	}
	
}

void SearchByName(PLIST list)
{
	cout << "탐색할 이름을 입력하세요 : ";
	char searchName[NAME_SIZE] = {};
	InputString(searchName, NAME_SIZE);

	PNODE	node = list->begin;

	while (node != NULL)
	{
		if (strcmp(node->studentData.name, searchName) == 0)
		{
			PrintStudentData(&node->studentData);
			system("pause");

			return;
		}

		node = node->nextNode;
	}

	cout << "ERROR : 일치하는 학생을 찾지 못했습니다." << endl;
	system("pause");
}

void SearchByNumber(PLIST list)
{
	cout << "탐색할 학번을 입력하세요 : ";
	int		searchNumber;
	searchNumber = InputInt();

	PNODE	node = list->begin;

	while (node != NULL)
	{
		if (node->studentData.number == searchNumber)
		{
			PrintStudentData(&node->studentData);
			system("pause");

			return;
		}

		node = node->nextNode;
	}

	cout << "ERROR : 일치하는 학생을 찾지 못했습니다." << endl;
	system("pause");
}

void Search(PLIST list)
{
	while (true)
	{
		system("cls");
		cout << "========== 3. 학생 탐색 ==========" << endl;

		cout << "1. 이름 검색" << endl;
		cout << "2. 학번 검색" << endl;
		cout << "3. 뒤로가기" << endl << endl;

		cout << "메뉴를 선택하세요 : ";

		int menu = InputInt();

		switch (menu)
		{
		case 1:
			SearchByName(list);
			break;
		case 2:
			SearchByNumber(list);
			break;
		case 3:
			break;
		}

		break;
	}
}

void Delete(PLIST list)
{
	system("cls");
	cout << "========== 4. 학생 삭제 ==========" << endl;

	cout << "삭제할 이름을 입력하세요 : ";
	char searchName[NAME_SIZE] = {};
	InputString(searchName, NAME_SIZE);

	PNODE	currNode = list->begin;

	while (currNode != NULL)
	{
		if (strcmp(currNode->studentData.name, searchName) == 0)
		{
			cout << currNode->studentData.name << " 학생이 삭제되었습니다." << endl;
			PNODE	nextNode = currNode->nextNode;
			PNODE	prevNode = currNode->prevNode;

			// 만약 이전 노드가 NULL이라면
			// 즉 시작 노드라면
			if (prevNode == NULL)
			{
				delete currNode;
				list->begin = nextNode;

				// 다음 노드가 헤드 노드가 된다.
				nextNode->prevNode = NULL;

				if (nextNode == NULL)
				{
					list->end = NULL;
				}
			}

			// 만약 이전 노드가 NULL이 아니라면 이전노드의 다음을 다음 노드와 연결
			else
			{
				delete currNode;
				
				// 다음 노드가 존재한다면
				if (nextNode != NULL)
				{
					nextNode->prevNode = prevNode;
					prevNode->nextNode = nextNode;
				}
				else
				{
					list->end = prevNode;
					prevNode->nextNode = NULL;
				}
			}

			--list->currentSize;
			system("pause");
			return;
		}

		// 해당 학생이 아니라면 현재 노드가 이전 노드가 된다.
		currNode = currNode->nextNode;
	}

	cout << "삭제할 학생을 찾을 수 없습니다." << endl;
	system("pause");
}

void Sort(PLIST list)
{
	system("cls");
	cout << "========== 5. 정렬하기 ==========" << endl;
	cout << "1. 학점 기준" << endl;
	cout << "2. 평균 기준" << endl;
	cout << "메뉴를 선택하세요. ";

	int menu = InputInt();

	if (menu <= SM_NONE || menu > SM_AVERAGE)
	{
		cout << "잘못 선택하였습니다." << endl;
		system("pause");
		return;
	}

	
	int i = 0;

	while (i <= list->currentSize)
	{
		PNODE	firstNode = list->begin;
		PNODE	secondNode = firstNode->nextNode;
		while (secondNode != NULL)
		{
			bool	swap = false;

			switch (menu)
			{
			case SM_NUMBER:
				if ((firstNode->studentData.number) > (secondNode->studentData.number))
				{
					swap = true;
				}

				break;
			case SM_AVERAGE:
				if ((firstNode->studentData.average) > (secondNode->studentData.average))
				{
					swap = true;
				}
				break;
			}

			if (swap == true)
			{
				PNODE	firstPrev = firstNode->prevNode;
				PNODE	firstNext = firstNode->nextNode;

				PNODE	secondPrev = secondNode->prevNode;
				PNODE	secondNext = secondNode->nextNode;


				// begin 설정
				if (firstNode == list->begin)
				{
					list->begin = secondNode;
				}

				PNODE	tempNode = firstNode;
				firstNode = secondNode;
				secondNode = tempNode;

				// 앞앞 노드
				if(firstPrev != NULL)
				{
					firstPrev->nextNode = firstNode;
				}

				// 뒤뒤 노드
				if (secondNext != NULL)
				{
					secondNext->prevNode = secondNode;
				}

				// 2번째 노드
				secondNode->nextNode = secondNext;
				secondNode->prevNode = firstNode;			

				// 1번째 노드
				firstNode->prevNode = firstPrev;
				firstNode->nextNode = secondNode;

				// end 설정
				if (firstNext == list->end)
				{
					list->end = secondNode;
				}

				firstNode = secondNode;
				secondNode = firstNode->nextNode;
			}
			else
			{
				firstNode = secondNode;
				secondNode = firstNode->nextNode;
			}
		}
		++i;
	}
	cout << "========= 정렬 되었습니다. =========" << endl;
	PrintListForwards(list);

	system("pause");
}

int main()
{
	LIST	list = {};

	InitList(&list);

	while (true)
	{
		int menu = PrintMenu();

		if (menu == MM_EXIT)
		{
			break;
		}

		switch (menu)
		{
		case MM_INSERT:
			Insert(&list);
			break;
		case MM_DELETE:
			Delete(&list);
			break;
		case MM_SEARCH:
			Search(&list);
			break;
		case MM_PRINT:
			PrintList(&list);
			break;
		case MM_SORT:
			Sort(&list);
			break;
		case MM_EXIT:
			break;
		}
	}

	ClearList(&list);

	return 0;
}

날짜 : 190520

주제 : 싱글 링크드 리스트로 학생관리 프로그램 만들기

설명 : 노드를 구조체로 만들고 그 구조체 안에 다음 구조체의 주소를 저장할 변수를 선언한다. 그 후 노드와 노드를 연결하는 것이 링크드 리스트이다. 장점은 노드의 최대 개수 같은 것들이 정해져 있지 않아서 마음대로 노드를 추가/제거할 수 있다. 따라서 고정크기인 배열의 단점을 보완할 수 있다. 단점은 연속된 메모리 공간이 아니기 때문에 추가하고 지울 경우 '메모리 단편화'가 발생할 수 있다.

 

#include <iostream>

using namespace std;

#define NAME_SIZE 32



/* 링크드 리스트 : 자료구조의 한 종류. 자료구조란 데이터를 저장하는 방법
링크드 리스트는 데이터 목록을 연결시켜서 접근할 수 있는 구조를 제공

특징 : 선형구조... 즉 배열처럼 특정요소에 바로 접근 불가. 무조건 앞에서부터 차례대로 들어가야한다.
노드를 추가할때 노드를 생성하고 마지막 노드에 연결. 따라서 개수의 제한이 없음.


노드 : 데이터를 저장하기 위한 공간
노드의 특징 : 다음노드에 연결가능(주소를 저장)
*/

enum MAIN_MENU
{
	MM_NONE,
	MM_INSERT,
	MM_DELETE,
	MM_SEARCH,
	MM_PRINT,
	MM_EXIT
};

// 학생 정보 구조체
typedef struct _tagStudent
{
	char	name[NAME_SIZE];
	int		number;
	int		kor;
	int		eng;
	int		math;
	int		total;
	float	average;
}STUDENT, *PSTUDENT;

// 노드 구조체
typedef struct _tagNode
{
	STUDENT studentData;
	_tagNode* nextNode;
}NODE, *PNODE;

// 리스트 구조체
typedef struct _tagList
{
	PNODE	begin;
	PNODE	end;
	int		currentSize;
	int		studentNumberStandard;
}LIST, *PLIST;

void InitList(PLIST list)
{
	// 포인터 초기화 방식
	list->begin = NULL;
	list->end = NULL;
	list->currentSize = 0;
}

int InputInt()
{
	int input;
	cin >> input;

	if (cin.fail())
	{
		cin.clear();
		cin.ignore(1024, '\n');
		return INT_MAX;
	}

	return input;
}

int PrintMenu()
{
	system("cls");
	cout << "1. 학생 추가" << endl;
	cout << "2. 학생 삭제" << endl;
	cout << "3. 학생 탐색" << endl;
	cout << "4. 학생 출력" << endl;
	cout << "5. 종료" << endl << endl;

	cout << "메뉴를 선택하세요 : " << endl;

	int input = InputInt();

	if (input <= MM_NONE || input > MM_EXIT)
	{
		return MM_NONE;
	}
	
	return input;
}

void InputString(char* string, int size)
{
	cin.clear();
	cin.ignore(1024, '\n');

	cin.getline(string, size - 1);
}

void Insert(PLIST list)
{
	system("cls");
	cout << "========== 1. 학생 추가 ==========" << endl;

	STUDENT student = {};

	cout << "이름 : ";
	InputString(student.name, NAME_SIZE);

	cout << "국어 점수 : ";
	student.kor = InputInt();

	cout << "영어 점수 : ";
	student.eng = InputInt();

	cout << "수학 점수 : ";
	student.math = InputInt();

	student.total = student.kor + student.eng + student.math;
	student.average = student.total / 3.f;

	++list->studentNumberStandard;
	++list->currentSize;
	student.number = list->studentNumberStandard;


	// 추가할 리스트 노드를 생성
	PNODE	node = new NODE;

	// 마지막에 추가되는 노드이기 때문에 그 뒤에 노드는 없음
	node->nextNode = NULL;

	// 입력받은 데이터를 노드에 저장
	node->studentData = student;

	// 헤드노드가 비어있으면 새로 생성된 노드가 헤드노드
	if (list->begin == NULL)
	{
		list->begin = node;
	}
	else
	{
		list->end->nextNode = node;
	}

	// 새로 생성된 노드가 마지막 노드
	list->end = node;
}

void ClearList(PLIST list)
{
	PNODE	node = list->begin;

	while (node != NULL)
	{
		// 다음 노드 저장
		PNODE	next = node->nextNode;
		
		// 현재 노드 삭제
		delete node;

		// 이제 다음 노드로
		node = next;
	}

	// 마지막으로 모두 초기화
	list->begin = NULL;
	list->end = NULL;
	list->currentSize = 0;
}

const void PrintStudentData(const PSTUDENT student)
{
	cout << "이름 : " << student->name << "\t학번 : " << student->number << endl;
	cout << "국어 점수 : " << student->kor << "\t영어 점수 : " << student->eng << endl;
	cout << "수학 점수 : " << student->math << endl;
	cout << "총 점 : " << student->total << "\t평 균 : " << student->average << endl << endl;
}

const void PrintList(const PLIST list)
{
	system("cls");
	cout << "========== 4. 학생 정보 출력 ==========" << endl;

	PNODE	node = list->begin;

	while (node != NULL)
	{
		PrintStudentData(&node->studentData);
		node = node->nextNode;
	}

	cout << "총 학생 수 : " << list->currentSize << endl;

	system("pause");
}

void SearchByName(PLIST list)
{
	cout << "탐색할 이름을 입력하세요 : ";
	char searchName[NAME_SIZE] = {};
	InputString(searchName, NAME_SIZE);

	PNODE	node = list->begin;

	while (node != NULL)
	{
		if (strcmp(node->studentData.name, searchName) == 0)
		{
			PrintStudentData(&node->studentData);
			system("pause");

			return;
		}

		node = node->nextNode;
	}

	cout << "ERROR : 일치하는 학생을 찾지 못했습니다." << endl;
	system("pause");
}

void SearchByNumber(PLIST list)
{
	cout << "탐색할 학번을 입력하세요 : ";
	int		searchNumber;
	searchNumber = InputInt();

	PNODE	node = list->begin;

	while (node != NULL)
	{
		if (node->studentData.number == searchNumber)
		{
			PrintStudentData(&node->studentData);
			system("pause");

			return;
		}

		node = node->nextNode;
	}

	cout << "ERROR : 일치하는 학생을 찾지 못했습니다." << endl;
	system("pause");
}

void Search(PLIST list)
{	
	while (true)
	{
		system("cls");
		cout << "========== 3. 학생 탐색 ==========" << endl;

		cout << "1. 이름 검색" << endl;
		cout << "2. 학번 검색" << endl;
		cout << "3. 뒤로가기" << endl << endl;

		cout << "메뉴를 선택하세요 : ";

		int menu = InputInt();

		switch (menu)
		{
		case 1:
			SearchByName(list);
			break;
		case 2:
			SearchByNumber(list);
			break;
		case 3:
			break;
		}

		break;
	}	
}

void Delete(PLIST list)
{
	system("cls");
	cout << "========== 4. 학생 삭제 ==========" << endl;

	cout << "삭제할 이름을 입력하세요 : ";
	char searchName[NAME_SIZE] = {};
	InputString(searchName, NAME_SIZE);

	PNODE	currNode = list->begin;
	PNODE	prevNode = NULL;

	while (currNode != NULL)
	{
		if (strcmp(currNode->studentData.name, searchName) == 0)
		{
			cout << currNode->studentData.name << " 학생이 삭제되었습니다." << endl;
			PNODE	nextNode = currNode->nextNode;

			// 만약 이전 노드가 NULL이라면
			// 즉 시작 노드라면
			if (prevNode == NULL)
			{
				delete currNode;
				list->begin = nextNode;

				if (nextNode == NULL)
				{
					list->end = NULL;
				}
			}

			// 만약 이전 노드가 NULL이 아니라면 이전노드의 다음을 다음 노드와 연결
			else
			{
				cout << currNode->studentData.name << " 학생이 삭제되었습니다." << endl;
				delete currNode;
				prevNode->nextNode = nextNode;

				if (nextNode == NULL)
				{
					list->end = prevNode;
				}
			}

			--list->currentSize;
			system("pause");
			return;
		}

		// 해당 학생이 아니라면 현재 노드가 이전 노드가 된다.
		prevNode = currNode;
		currNode = currNode->nextNode;
	}

	cout << "삭제할 학생을 찾을 수 없습니다." << endl;
	system("pause");
}

int main()
{
	LIST	list = {};

	InitList(&list);

	while (true)
	{
		int menu = PrintMenu();

		if (menu == MM_EXIT)
		{
			break;
		}

		switch (menu)
		{
		case MM_INSERT:
			Insert(&list);
			break;
		case MM_DELETE:
			Delete(&list);
			break;
		case MM_SEARCH:
			Search(&list);
			break;
		case MM_PRINT:
			PrintList(&list);
			break;
		}
	}

	ClearList(&list);

	return 0;
}
/* 숙제 : 더블 링크드 리스트로 학생관리 프로그램 만들기 */

 

 

 

 

날짜 : 190517

주제 : TextRPG (플레이어, 몬스터, 전투, 상점)

특징 : 

1. 강의 20화에서 했던 메뉴 구현을 적용시켜서 턴제 Text RPG를 구현.

2. 상점에서 무기 팔기 기능은 아직 구현하지 않음

3. 지도 easy, normal, hard를 선택함에 따라 나오는 몬스터가 정해져있다.

#include <iostream>
#include "time.h"
#include "stdlib.h"

using namespace std;

enum MAIN_MENU
{
	MM_NONE,
	MM_MAP,
	MM_STORE,
	MM_INVENTORY,
	MM_EXIT
};

enum MAP_TYPE
{
	MT_NONE,
	MT_EASY,
	MT_NORMAL,
	MT_HARD,
	MT_BACK
};

enum CLASS
{
	CLASS_NONE,
	CLASS_KNIGHT,
	CLASS_ARCHER,
	CLASS_WIZARD,
	CLASS_END
};

enum BATTLE
{
	BATTLE_NONE,
	BATTLE_ATTACK,
	BATTLE_BACK
};

enum ITEM_TYPE
{
	IT_NONE,
	IT_WEAPON,
	IT_ARMOR,
	IT_BACK
};

enum STORE_MENU
{
	SM_NONE,
	SM_WEAPON,
	SM_ARMOR,
	SM_BACK
};

enum STORE_MENU_2
{
	SM2_NONE,
	SM2_LIST1,
	SM2_LIST2,
	SM2_LIST3,
	SM2_BACK
};

#define NAME_SIZE			32
#define ITEM_DESC_LENGTH	512
#define INVENTORY_MAX		6
#define STORE_WEAPON_MAX	3
#define STORE_ARMOR_MAX		3

struct _tagItem
{
	char	  name[NAME_SIZE];
	char	  typeName[NAME_SIZE];
	ITEM_TYPE type;
	int		  min;
	int		  max;
	int		  price;
	int		  sell;
	char	  description[ITEM_DESC_LENGTH];
};

struct _tagInventory
{
	_tagItem itemArr[INVENTORY_MAX];
	int		itemCount;
	int		gold;
	_tagItem weapon;
	_tagItem armor;
};

struct _tagPlayer
{
	char	name[NAME_SIZE];
	char	className[NAME_SIZE];
	CLASS	Class;
	int		attackMin;
	int		attackMax;
	int		armorMin;
	int		armorMax;
	int		hp;
	int		hpMax;
	int		mp;
	int		mpMax;
	int		exp;
	int		level;
	_tagInventory inventory;
};


struct _tagMonster
{
	char	name[NAME_SIZE];
	int		attackMin;
	int		attackMax;
	int		armorMin;
	int		armorMax;
	int		hp;
	int		hpMax;
	int		mp;
	int		mpMax;
	int		level;
	int		exp;
	int		goldMin;
	int		goldMax;

};

int main()
{
	srand((unsigned int)time(0));

	// 플레이어 정보 설정
	_tagPlayer player = {};

	cout << "이름 : ";
	cin.getline(player.name, NAME_SIZE - 1);

	int selectedClass = CLASS_NONE;
	while (true)
	{
		system("cls");
		cout << "1. 기사" << endl;
		cout << "2. 궁사" << endl;
		cout << "3. 마법사" << endl;
		cout << "클래스를 고르시오 : ";
		cin >> selectedClass;

		if (cin.fail())
		{
			cin.clear();
			cin.ignore(1024, '\n');
			continue;
		}

		else if (selectedClass <= CLASS_NONE || selectedClass >= CLASS_END)
		{
			selectedClass = CLASS_NONE;
			continue;
		}
		
		break;
	}
	
	player.level = 1;
	player.exp = 0;
	player.inventory.gold = 1000000;
	player.Class = (CLASS)selectedClass;

	
	switch (player.Class)
	{
	case CLASS_KNIGHT:
		strcpy_s(player.className, "기사");
		player.attackMin = 5;
		player.attackMax = 10;
		player.armorMin = 15;
		player.armorMax = 20;
		player.hpMax = 500;
		player.hp = 500;
		player.mpMax = 100;
		player.mp = 100;
		break;
	case CLASS_ARCHER:
		strcpy_s(player.className, "궁수");
		player.attackMin = 10;
		player.attackMax = 15;
		player.armorMin = 10;
		player.armorMax = 15;
		player.hpMax = 400;
		player.hp = 400;
		player.mpMax = 200;
		player.mp = 200;
		break;
	case CLASS_WIZARD:
		strcpy_s(player.className, "마법사");
		player.attackMin = 15;
		player.attackMax = 20;
		player.armorMin = 5;
		player.armorMax = 10;
		player.hpMax = 300;
		player.hp = 300;
		player.mpMax = 300;
		player.mp = 300;
		break;
	}

	// 몬스터 생성
	_tagMonster	monsterArr[MT_BACK - 1] = {};

	// 고블린 생성
	strcpy_s(monsterArr[0].name, "고블린");
	monsterArr[0].attackMin = 20;
	monsterArr[0].attackMax = 30;
	monsterArr[0].armorMin = 2;
	monsterArr[0].armorMax = 5;
	monsterArr[0].hpMax = 100;
	monsterArr[0].hp = 100;
	monsterArr[0].mpMax = 10;
	monsterArr[0].mp = 10;
	monsterArr[0].level = 1;
	monsterArr[0].exp = 10;
	monsterArr[0].goldMin = 500;
	monsterArr[0].goldMax = 800;

	// 트롤
	strcpy_s(monsterArr[1].name, "트롤");
	monsterArr[1].attackMin = 80;
	monsterArr[1].attackMax = 130;
	monsterArr[1].armorMin = 60;
	monsterArr[1].armorMax = 90;
	monsterArr[1].hpMax = 2000;
	monsterArr[1].hp = 2000;
	monsterArr[1].mpMax = 100;
	monsterArr[1].mp = 100;
	monsterArr[1].level = 5;
	monsterArr[1].exp = 80;
	monsterArr[1].goldMin = 4000;
	monsterArr[1].goldMax = 7000;

	// 드래곤
	strcpy_s(monsterArr[2].name, "드래곤");
	monsterArr[2].attackMin = 250;
	monsterArr[2].attackMax = 500;
	monsterArr[2].armorMin = 200;
	monsterArr[2].armorMax = 400;
	monsterArr[2].hpMax = 30000;
	monsterArr[2].hp = 30000;
	monsterArr[2].mpMax = 20000;
	monsterArr[2].mp = 20000;
	monsterArr[2].level = 10;
	monsterArr[2].exp = 30000;
	monsterArr[2].goldMin = 30000;
	monsterArr[2].goldMax = 70000;

	// 상점에서 판매할 아에팀 목록을 생성한다.

	// 무기 리스트
	_tagItem	storeWeaponArr[STORE_WEAPON_MAX] = {};
	// 요우무의 단검
	strcpy_s(storeWeaponArr[0].name, "요우무의 단검");
	strcpy_s(storeWeaponArr[0].typeName, "무기");
	storeWeaponArr[0].type = IT_WEAPON;
	storeWeaponArr[0].min = 100;
	storeWeaponArr[0].max = 500;
	storeWeaponArr[0].price = 1000;
	storeWeaponArr[0].sell = storeWeaponArr[0].price * 0.6f;
	strcpy_s(storeWeaponArr[0].description, 
		"요우무라는 장인이 발레리안 스틸로 만든 고급 단검"
	);
	// 슈렐리아의 장검
	strcpy_s(storeWeaponArr[1].name, "슈렐리아의 장검");
	strcpy_s(storeWeaponArr[1].typeName, "무기");
	storeWeaponArr[1].type = IT_WEAPON;
	storeWeaponArr[1].min = 1000;
	storeWeaponArr[1].max = 1500;
	storeWeaponArr[1].price = 20000;
	storeWeaponArr[1].sell = storeWeaponArr[1].price * 0.6f;
	strcpy_s(storeWeaponArr[1].description,
		"슈렐리아라는 나라에서 황실 경비병들이 사용하는 무기"
	);
	// 레이피어
	strcpy_s(storeWeaponArr[2].name, "레이피어");
	strcpy_s(storeWeaponArr[2].typeName, "무기");
	storeWeaponArr[2].type = IT_WEAPON;
	storeWeaponArr[2].min = 2000;
	storeWeaponArr[2].max = 3000;
	storeWeaponArr[2].price = 500000;
	storeWeaponArr[2].sell = storeWeaponArr[2].price * 0.6f;
	strcpy_s(storeWeaponArr[2].description,
		"요정족이 만들었다고 소문으로만 전해지는 전설의 검. 가벼운 것이 특징"
	);

	// 갑옷 리스트
	_tagItem	storeArmorArr[STORE_WEAPON_MAX] = {};
	// 징박힌 갑옷
	strcpy_s(storeArmorArr[0].name, "징박힌 값옷");
	strcpy_s(storeArmorArr[0].typeName, "갑옷");
	storeArmorArr[0].type = IT_ARMOR;
	storeArmorArr[0].min = 105;
	storeArmorArr[0].max = 505;
	storeArmorArr[0].price = 1005;
	storeArmorArr[0].sell = storeWeaponArr[0].price * 0.6f;
	strcpy_s(storeArmorArr[0].description,
		"고급 가죽 갑옷에 징을 박아 방어력을 더함"
	);
	// 판금 갑옷
	strcpy_s(storeArmorArr[1].name, "판금 갑옷");
	strcpy_s(storeArmorArr[1].typeName, "갑옷");
	storeArmorArr[1].type = IT_ARMOR;
	storeArmorArr[1].min = 1005;
	storeArmorArr[1].max = 1505;
	storeArmorArr[1].price = 20005;
	storeArmorArr[1].sell = storeArmorArr[1].price * 0.6f;
	strcpy_s(storeArmorArr[1].description,
		"판금으로 만들어진 갑옷"
	);
	// 드래곤 스케일 갑옷
	strcpy_s(storeArmorArr[2].name, "레이피어");
	strcpy_s(storeArmorArr[2].typeName, "갑옷");
	storeArmorArr[2].type = IT_ARMOR;
	storeArmorArr[2].min = 2005;
	storeArmorArr[2].max = 3005;
	storeArmorArr[2].price = 500005;
	storeArmorArr[2].sell = storeArmorArr[2].price * 0.6f;
	strcpy_s(storeArmorArr[2].description,
		"전설의 용을 잡아 그 가죽으로 만든 갑옷"
	);

	

	// 메인 화면
	while (true)
	{
		system("cls");
		cout << endl << "========== 메인 메뉴 ==========" << endl << endl;
		cout << "1. 지도 " << endl;
		cout << "2. 상점 " << endl;
		cout << "3. 가방 " << endl;
		cout << "4. 종료 " << endl;
		cout << "메뉴를 선택하세요 : ";
		int menu;
		cin >> menu;

		if (cin.fail())
		{
			cin.clear();
			cin.ignore(1024, '\n');
			continue;
		}

		if (menu == MM_EXIT)
		{
			break;
		}

		switch (menu)
		{
		case MM_MAP:
			while (true)
			{
				system("cls");
				cout << endl << "========== 맵 종류 ==========" << endl<< endl;
				cout << "1. 쉬움" << endl;
				cout << "2. 보통" << endl;
				cout << "3. 어려움" << endl;
				cout << "4. 뒤로가기" << endl;
				cout << "맵을 선택하세요 : ";
				cin >> menu;

				if (cin.fail())
				{
					cin.clear();
					cin.ignore(1024, '\n');
					continue;
				}

				if (menu == MT_BACK)
				{
					// 맵 선택 화면에서 빠져나간다
					break;
				}

				// 선택한 메뉴에서 1을 빼주면 몬스터 배열의 인덱스가 된다.
					// 이렇게 해당 맵의 몬스터를 설정
				_tagMonster monster = monsterArr[menu - 1];

				// 전투
				while (true)
				{
					system("cls");
					switch(menu)
					{
					case MT_EASY:
						cout << "********** 쉬움 **********" << endl;
						break;
					case MT_NORMAL:
						cout << "********** 보통 **********" << endl;
						break;
					case MT_HARD:
						cout << "********** 어려움 **********" << endl;
						break;
					}

					// 플레이어 정보를 출력
					cout << "========== 플레이어 ==========" << endl;
					cout << "이름 : " << player.name << "\t직업 : " << player.className << endl;
					cout << "레벨 : " << player.level << "\t경험치 : " << player.exp << endl;
					cout << "공격력 : " << player.attackMin << " ~ " << player.attackMax << endl;
					cout << "방어력 : " << player.armorMin << " ~ " << player.armorMax << endl;
					cout << "체력 : " << player.hp << " / " << player.hpMax << "\t마나 : " << player.mp << " / " << player.mpMax << endl;
					cout << "소지금 : " << player.inventory.gold << endl << endl;

					// 몬스터 정보를 출력
					cout << "========== 몬스터 ==========" << endl;
					cout << "이름 : " << monster.name << "\t레벨 : " << monster.level << endl;
					cout << "공격력 : " << monster.attackMin << " ~ " << monster.attackMax << endl;
					cout << "방어력 : " << monster.armorMin << " ~ " << monster.armorMax << endl;
					cout << "체력 : " << monster.hp << " / " << monster.hpMax << "\t마나 : " << monster.mp << " / " << monster.mpMax << endl;
					cout << "획득 경험치 : " << monster.exp << "\t획득 골드 : " << monster.goldMin << " ~ " << monster.goldMax << endl << endl;

					cout << "========== 행동 ==========" << endl;
					cout << "1. 공격" << endl;
					cout << "2. 도망가기" << endl;
					cout << "행동을 선택하세요." << endl;
					cin >> menu;

					if (cin.fail())
					{
						cin.clear();
						cin.ignore(1024, '\n');
						continue;
					}

					else if (menu == BATTLE_BACK)
					{
						break;
					}

					switch (menu)
					{
					case BATTLE_ATTACK:
						int attack = rand() % (player.attackMax - player.attackMin + 1) + (player.attackMin);
						int armor = rand() % (monster.armorMax - monster.armorMin + 1) + (monster.armorMin);

						int damage = attack - armor;

						// 삼항연산자 : 조건식 ? true일 때 값, : false 일 때 값이 된다.
						// 최소 데미지를 음수가 되지 않고 1로 설정
						damage = damage < 1 ? 1 : damage;

						// 몬스터 HP를 감소시킨다.
						monster.hp -= damage;

						cout << player.name << "가 " << monster.name << "에게 " << damage << "의 피해를 입혔습니다." << endl;

						// 몬스터가 죽음
						if (monster.hp <= 0)
						{
							cout << monster.name << " 몬스터가 사망하였습니다." << endl;

							player.exp += monster.exp;
							int gold = (rand() % monster.goldMax - monster.goldMin + 1) + monster.goldMin;
							player.inventory.gold += gold;

							cout << monster.exp << "의 경험치를 획득하였습니다." << endl;
							cout << gold << "의 골드를 획득하였습니다." << endl;

							// 몬스터 스탯을 초기화
							monster.hp = monster.hpMax;
							monster.mp = monster.mpMax;

							system("pause");
							break;
						}

						// 몬스터가 살아있으면 플레이어를 공격
						attack = rand() % (monster.attackMax - monster.attackMin + 1) + (monster.attackMin);
						armor = rand() % (player.armorMax - player.armorMin + 1) + (player.armorMin);

						damage = attack - armor;

						damage = damage < 1 ? 1 : damage;

						// 플레이어 HP를 감소시킨다.
						player.hp -= damage;

						cout << monster.name << "가 " << player.name << "에게 " << damage << "의 피해를 입혔습니다." << endl;

						// 플레이어가 죽었을 경우
						if (player.hp <= 0)
						{
							cout << "플레이어가 " << monster.name << "에게 사망하였습니다." << endl;

							// 죽으면 경험치와 골드가 10% 깎인다
							cout << (player.exp * 0.1f) << "경험치를 잃었습니다." << endl;
							cout << (player.inventory.gold * 0.1f) << "골드를 잃었습니다." << endl;
							
							player.exp = player.exp - (player.exp * 0.1f);
							player.inventory.gold = player.inventory.gold - (player.inventory.gold * 0.1f);

							// 플레이어 부활
							player.hp = player.hpMax;
							player.mp = player.mpMax;
						}
						system("pause");

						break;
					}
				}
			}
			break;
		case MM_STORE:
			while (true)
			{
				system("cls");

				cout << "========== 상점 ==========" << endl;
				cout << "1. 무기 상점" << endl;
				cout << "2. 방어구 상점" << endl;
				cout << "3. 뒤로 가기" << endl;
				cout << "상점을 선택하세요 : ";
				cin >> menu;

				if (cin.fail())
				{
					cin.clear();
					cin.ignore(1024, '\n');
					continue;
				}

				else if (menu == SM_BACK)
				{
					break;
				}
 					
				else if (menu < SM_NONE || menu > SM_BACK)
				{
					cout << "잘못된 입력입니다. 다시 시도해주세요." << endl;
					system("pause");
					continue;
				}				

				switch (menu)
				{
				case SM_WEAPON:
					while (true)
					{
						system("cls");
						cout << "=========== 무기 상점 ==========" << endl;
						for (int i = 0; i < STORE_WEAPON_MAX; ++i)
						{
							cout << i + 1 << "번 무기 : " << storeWeaponArr[i].name << endl;
							cout << "공격력 : " << storeWeaponArr[i].min << " ~ " << storeWeaponArr[i].max << endl;
							cout << "구매 가격 : " << storeWeaponArr[i].price << " 골드" << endl;
							cout << "판매 가격 : " << storeWeaponArr[i].sell << " 골드" << endl;
							cout << "특징 : " << storeWeaponArr[i].description << endl;
							cout << "-----------------------------------------" << endl;
						}
						cout << "뒤로가기 : 4번을 누르세요." << endl;
						cout << "구매할 아이템을 선택하시오 : ";
						cout << "현재 소지금 : " << player.inventory.gold << endl;
						cout << "가방 남은 자리 : " << int(INVENTORY_MAX - player.inventory.itemCount) << " / " << INVENTORY_MAX << endl; 

						cin >> menu;

						if (cin.fail())
						{
							cin.clear();
							cin.ignore(1024, '\n');
							continue;
						}

						else if (menu < SM2_NONE || menu > SM2_BACK)
						{
							cout << "잘못된 입력입니다. 다시 시도해주세요." << endl;
							system("pause");
							continue;
						}

						switch (menu)
						{							
						case SM2_LIST1:
							if (player.inventory.itemCount >= INVENTORY_MAX)
							{
								cout << "인벤토리가 모두 찼습니다. 상점을 이용할 수 없습니다." << endl;
								system("pause");
								continue;
							}
							if (player.inventory.gold < storeWeaponArr[0].price)
							{
								cout << "소지금이 부족합니다." << endl;
								system("pause");
								continue;
							}
							cout << storeWeaponArr[0].name << "을 구매하셨습니다." << endl;
							player.inventory.gold -= storeWeaponArr[0].price;

							// 무기 바로 장착
							player.attackMin += storeWeaponArr[0].min;
							player.attackMax += storeWeaponArr[0].max;

							// inventory에 아이템 넣기
							player.inventory.itemArr[player.inventory.itemCount++] = storeWeaponArr[0];

							cout << "현재 소지금 : " << player.inventory.gold << endl;
							break;
						case SM2_LIST2:
							if (player.inventory.itemCount >= INVENTORY_MAX)
							{
								cout << "인벤토리가 모두 찼습니다. 상점을 이용할 수 없습니다." << endl;
								system("pause");
								continue;
							}
							if (player.inventory.gold < storeWeaponArr[1].price)
							{
								cout << "소지금이 부족합니다." << endl;
								system("pause");
								continue;
							}
							cout << storeWeaponArr[1].name << "을 구매하셨습니다." << endl;
							player.inventory.gold -= storeWeaponArr[1].price;

							// inventory에 아이템 넣기
							player.inventory.itemArr[player.inventory.itemCount++] = storeWeaponArr[1];

							// 무기 바로 장착
							player.attackMin += storeWeaponArr[1].min;
							player.attackMax += storeWeaponArr[1].max;

							cout << "현재 소지금 : " << player.inventory.gold << endl;
							break;
						case SM2_LIST3:
							if (player.inventory.itemCount >= INVENTORY_MAX )
							{
								cout << "인벤토리가 모두 찼습니다. 상점을 이용할 수 없습니다." << endl;
								system("pause");
								continue;
							}
							if (player.inventory.gold < storeWeaponArr[2].price)
							{
								cout << "소지금이 부족합니다." << endl;
								system("pause");
								continue;
							}
							cout << storeWeaponArr[2].name << "을 구매하셨습니다." << endl;
							player.inventory.gold -= storeWeaponArr[2].price;

							// 무기 바로 장착
							player.attackMin += storeWeaponArr[2].min;
							player.attackMax += storeWeaponArr[2].max;

							// inventory에 아이템 넣기
							player.inventory.itemArr[player.inventory.itemCount++] = storeWeaponArr[2];

							cout << "현재 소지금 : " << player.inventory.gold << endl;
							break;
						case SM2_BACK:	
							break;
						}	
						break;
					}			

					break;
				case SM_ARMOR:					
					while (true)
					{
						system("cls");
						cout << "=========== 갑옷 상점 ==========" << endl;
						for (int i = 0; i < STORE_ARMOR_MAX; ++i)
						{
							cout << i + 1 << "번 무기 : " << storeArmorArr[i].name << endl;
							cout << "공격력 : " << storeArmorArr[i].min << " ~ " << storeWeaponArr[i].max << endl;
							cout << "구매 가격 : " << storeArmorArr[i].price << " 골드" << endl;
							cout << "판매 가격 : " << storeArmorArr[i].sell << " 골드" << endl;
							cout << "특징 : " << storeArmorArr[i].description << endl;
							cout << "-----------------------------------------" << endl;
						}

						cout << "구매할 아이템을 선택하시오 : ";
						cin >> menu;

						if (cin.fail())
						{
							cin.clear();
							cin.ignore(1024, '\n');
							continue;
						}

						else if (menu < SM2_NONE || menu > SM2_BACK)
						{
							cout << "잘못된 입력입니다. 다시 시도해주세요." << endl;
							system("pause");
							continue;
						}

						switch (menu)
						{							
						case SM2_LIST1:
							if (player.inventory.itemCount >= INVENTORY_MAX)
							{
								cout << "인벤토리가 모두 찼습니다. 상점을 이용할 수 없습니다." << endl;
								system("pause");
								continue;
							}
							if (player.inventory.gold < storeArmorArr[0].price)
							{
								cout << "소지금이 부족합니다." << endl;
								system("pause");
								continue;
							}
							cout << storeArmorArr[0].name << "을 구매하셨습니다." << endl;
							player.inventory.gold -= storeArmorArr[0].price;

							// 무기 바로 장착
							player.armorMin += storeArmorArr[0].min;
							player.armorMax += storeArmorArr[0].max;

							// inventory에 아이템 넣기
							player.inventory.itemArr[player.inventory.itemCount++] = storeArmorArr[0];

							cout << "현재 소지금 : " << player.inventory.gold << endl;
							break;
						case SM2_LIST2:
							if (player.inventory.itemCount >= INVENTORY_MAX)
							{
								cout << "인벤토리가 모두 찼습니다. 상점을 이용할 수 없습니다." << endl;
								system("pause");
								continue;
							}
							if (player.inventory.gold < storeArmorArr[1].price)
							{
								cout << "소지금이 부족합니다." << endl;
								system("pause");
								continue;
							}
							cout << storeArmorArr[1].name << "을 구매하셨습니다." << endl;
							player.inventory.gold -= storeArmorArr[1].price;

							// inventory에 아이템 넣기
							player.inventory.itemArr[player.inventory.itemCount++] = storeArmorArr[1];

							// 무기 바로 장착
							player.armorMin += storeArmorArr[1].min;
							player.armorMax += storeArmorArr[1].max;

							cout << "현재 소지금 : " << player.inventory.gold << endl;
							break;
						case SM2_LIST3:
							if (player.inventory.itemCount >= INVENTORY_MAX)
							{
								cout << "인벤토리가 모두 찼습니다. 상점을 이용할 수 없습니다." << endl;
								system("pause");
								continue;
							}
							if (player.inventory.gold < storeArmorArr[2].price)
							{
								cout << "소지금이 부족합니다." << endl;
								system("pause");
								continue;
							}
							cout << storeArmorArr[2].name << "을 구매하셨습니다." << endl;
							player.inventory.gold -= storeArmorArr[2].price;

							// 무기 바로 장착
							player.armorMin += storeArmorArr[2].min;
							player.armorMax += storeArmorArr[2].max;

							// inventory에 아이템 넣기
							player.inventory.itemArr[player.inventory.itemCount++] = storeArmorArr[2];

							cout << "현재 소지금 : " << player.inventory.gold << endl;
							break;
						case SM2_BACK:
							break;
						}
						break;
					}
					break;
				}

				cout << "========== 플레이어 ==========" << endl;
				cout << "이름 : " << player.name << "\t직업 : " << player.className << endl;
				cout << "레벨 : " << player.level << "\t경험치 : " << player.exp << endl;
				cout << "공격력 : " << player.attackMin << " ~ " << player.attackMax << endl;
				cout << "방어력 : " << player.armorMin << " ~ " << player.armorMax << endl;
				cout << "체력 : " << player.hp << " / " << player.hpMax << "\t마나 : " << player.mp << " / " << player.mpMax << endl;
				cout << "소지금 : " << player.inventory.gold << endl << endl;
				cout << "가방 남은 자리 : " << int(INVENTORY_MAX - player.inventory.itemCount) << " / " << INVENTORY_MAX << endl;

				system("pause");
				continue;
			}

			break;
		case MM_INVENTORY:
			system("cls");
			cout << "========== 현재 소지품 목록 ==========" << endl;
			cout << "가방 남은 자리 : " << int(INVENTORY_MAX - player.inventory.itemCount) << " / " << INVENTORY_MAX << endl;
			for (int i = 0; i < player.inventory.itemCount; ++i)
			{
				cout << player.inventory.itemArr[i].name << endl;
			}

			cout << "골드 : " << player.inventory.gold << endl;
			system("pause");
			break;
		}
	}


	return 0;
}

/*
숙제
1. 무기상점에서 판매할 아이템 목록 만들기 그리고 출력
2. 출력할 대 앞에 번호를 부여해주고 번호를 입력, 구매
3. 인벤토리기 꽉 차면 구매 불가
4. 가방을 선택하면 가방 안의 아이템을 보여준다.
*/

날짜 : 190516

주제 : 구조체와 문자열 함수를 통해서 학생 관리 시스템을 만든다.

특징 :

cin의 기능과 c의 문자열 함수들을 통해 구조체 문자열에 문자를 입력한다. 

메뉴 기능을 구현한다.

 

#include <iostream>

using namespace std;

#define NAME_SIZE 32
#define ADDRESS_SIZE 128
#define PHONE_SIZE 14 
#define STUDENT_MAX 10

struct _tagStudent
{
	char	name[NAME_SIZE];
	char	address[ADDRESS_SIZE];
	char	phoneNumber[PHONE_SIZE];
	int number;
	int kor;
	int eng;
	int math;
	int total;
	float average;
};

enum MENU
{
	MENU_NONE,
	MENU_INSERT,
	MENU_DELETE,
	MENU_SEARCH,
	MENU_OUTPUT,
	MENU_EXIT
};

int main()
{
	_tagStudent studentArr[STUDENT_MAX] = {};
	int studentCount = 0;
	int studentNum = 1;
	char	searchName[NAME_SIZE] = {};
	char	deleteName[NAME_SIZE] = {};

	while (true)
	{
		system("cls");

		// 메뉴를 출력
		cout << "1. 학생 등록" << endl;
		cout << "2. 학생 삭제" << endl;
		cout << "3. 학생 탐색" << endl;
		cout << "4. 학생 출력" << endl;
		cout << "5. 종료" << endl;
		cout << "메뉴를 선택하세요 : ";
		int menu;
		cin >> menu;

		// cin은 만약 오른쪽에 실수로 문자를 입력할 경우 에러 발생
		// 그렇기 때문에 예외처리를 해준다.
		// 에러가 발생하면 cin 내부의 에러버퍼를 비워주고
		// (입력버퍼 : 입력한 값을 저장해놓고 그 값을 변수에 넣어주는 역할)
		// 이 입력버퍼에 '\n'이 남아있으므로 버퍼를 순회하여 '\n'를 지워준다.
		
		// fail() 입력 에러가 발생했을 경우 true 반환
		if (cin.fail())
		{
			cin.clear(); // 에러버퍼 비우기

			// 입력버퍼에 '/n'이 남아 있으므로 입력버퍼를 검색하여 '\n'를 삭제
			// 첫번째 : 검색하고자 하는 버퍼 크기를 지정. 넉넉히 1024
			// 두번쨰 : 찾고자하는 문자를 넣어준다. 그 문자까지 이동해서 삭제
			cin.ignore(1024, '\n');
			continue;
		}

		if (menu == MENU_EXIT)
			break;

		switch (menu)
		{
		case MENU_INSERT:
			system("cls");
			cout << "========== 학생 추가 ==========" << endl;
			// 학생 수가 모두 찬 경우
			if (studentCount == STUDENT_MAX)
				break;

			// 학생정보를 추가(이름, 주소, 학번, 전화번호)
			// 국어 영어 수학 점수는 입력받는다. 학번, 총점, 평균은 연산된다
			cout << "이름 : ";
			cin >> studentArr[studentCount].name;

			// cin과 cin.getline을 같이 쓸 때 주의해야 한다... 
			// cin이기 때문에 콘솔에서 입력을 하고 엔터를 친 값을 입력한다.
			// 그러면 cin의 eofbit가 unset 상태로 된다.
			// 그리고 cin의 위치는 cin >> studentArr[studentNum].name;의 가장 마지막인
			// 그래서 '\n'을 가르킨다. 이상태에서 getline을 하면 '\n'을 읽으려고 한다.
			// 따라서 '\n'을 찾아서 지워야함.
			// 또는 대신 cin >> std::ws;을 써서 '\n'을 자연스레 넘어가서
			// 다음 오는 getline을 정상적으로 읽는다.
			cin.ignore(1024, '\n');

			cout << "주소 : ";
			// getline()은 뉴라인 문자를 입력스트림에서 버린다.
			cin.getline(studentArr[studentCount].address, ADDRESS_SIZE);

			cout << "전화번호 : " << endl;
			cin.getline(studentArr[studentCount].phoneNumber, PHONE_SIZE);

			cout << "국어 점수 : ";
			cin >> studentArr[studentCount].kor;

			cout << "영어 점수 : ";
			cin >> studentArr[studentCount].eng;

			cout << "수학 점수 : ";
			cin >> studentArr[studentCount].math;

			studentArr[studentCount].total = studentArr[studentCount].kor + studentArr[studentCount].eng + studentArr[studentCount].math;
			studentArr[studentCount].average = studentArr[studentCount].total / 3.f;

			studentArr[studentCount].number = studentNum;

			++studentNum;
			++studentCount;
			break;
		case MENU_DELETE:
			cout << "========== 학생 삭제 ==========" << endl;
			cout << "지울 이름을 입력하세요 : ";
			cin >> std::ws;
			cin.getline(deleteName, NAME_SIZE);

			// 등록되어 있는 학생 수만큼 반복
			for (int i = 0; i < studentCount; ++i)
			{
				// 학생을 찾았을 경우
				if (strcmp(studentArr[i].name, deleteName) == 0)
				{
					for (int j = i; j < studentCount- 1; ++j)
					{
						studentArr[i] = studentArr[i + 1];
					}
					--studentCount;					
					
					cout << "학생 정보가 삭제되었습니다." << endl;
					break;
				}
			}

			break;
		case MENU_SEARCH:
			system("cls");

			cout << "========== 학생 탐색 ==========" << endl;
			
			//cin.ignore(1024, '\n');
			cout << "탐색할 이름을 입력하세요 : ";
			cin >> std::ws;
			
			cin.getline(searchName, NAME_SIZE);

			// 등록되어 있는 학생 수만큼 반복
			for (int i = 0; i < studentCount; ++i)
			{
				// 학생을 찾았을 경우
				if (strcmp(studentArr[i].name, searchName) == 0)
				{
					cout << "이름 : " << studentArr[i].name << endl;
					cout << "전화번호 : " << studentArr[i].phoneNumber << endl;
					cout << "주소 : " << studentArr[i].address << endl;
					cout << "학번 : " << studentArr[i].number << endl;
					cout << "국어 : " << studentArr[i].kor << endl;
					cout << "영어 : " << studentArr[i].eng << endl;
					cout << "수학 : " << studentArr[i].math << endl;
					cout << "총점 : " << studentArr[i].total << endl;
					cout << "평균 : " << studentArr[i].average << endl << endl;
					break;
				}
			}
			break;
		case MENU_OUTPUT:
			system("cls");
			cout << "========== 학생 출력 ==========" << endl;
			// 등록된 학생 수만큼 반복
			for (int i = 0; i < studentCount; ++i)
			{
				cout << "이름 : " << studentArr[i].name << endl;
				cout << "전화번호 : " << studentArr[i].phoneNumber << endl;
				cout << "주소 : " << studentArr[i].address << endl;
				cout << "학번 : " << studentArr[i].number << endl;
				cout << "국어 : " << studentArr[i].kor << endl;
				cout << "영어 : " << studentArr[i].eng << endl;
				cout << "수학 : " << studentArr[i].math << endl;
				cout << "총점 : " << studentArr[i].total << endl;
				cout << "평균 : " << studentArr[i].average << endl << endl;

			}

			break;
		default:
			cout << "메뉴를 잘못 선택했습니다." << endl;
			break;
		}

		system("pause");
	}

	return 0;
}

/* 숙제 : 관리 프로그램
도서 대여 프로그램 만들기
1. 책 등록
2. 책 대여
3. 책 반납
4. 책 목록
4. 종료

책 구조체는 책이름, 대여금액, 책번호, 대여여부
책목록을 선택하면 책 정보를 모두 출력
*/

 

 

 

+ Recent posts