코딩 63일차 C/C++

마스터피스·2024년 2월 7일
0

C/ C++ 

목록 보기
35/35
post-thumbnail

영문 타자연습 프로그램 만들기

1) 헤더파일

1-1)Header.h

#ifndef  __HEADER_H__
#define __HEADER_H__


#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <string>
#include <vector>

#include "TypingGameHelper.h"



#endif // ! __HEADER_H__

1-2) TypingGameHelper.h

#ifndef __TYPING_GAME_HELPER_H__
#define __TYPING_GAME_HELPER_H__

#include <Windows.h>
#include <cstdio>
#include <string>

void putStringOnPosition(int x, int y, const std::string& content);

void showConsoleCursor(bool showFlag);

void clear();

#endif

1-3) TypingSystem.h

#ifndef __TYPING_SYSTEM_H__
#define __TYPING_SYSTEM_H__
#include "Header.h"
#include "User.h"

class TypingSystem {
private:
    //문장들이 저장됨. vector로 받아서 문장의 갯수 제한 없앰
    std::vector<std::string> sentences;

    // 현재 사용자
    User* currentUser = nullptr;

public:

    //디스크로부터 데이터를 읽어와서 문장 리스트를 초기화 하는 함수
    void loadData();

    //데이터 출력 함수
    void printData();

    //시작하는 함수
    void start();

    //메뉴 출력함수
    void printMenu();

    // 메뉴를 입력받을때 문자 하나를 입력받기.
    char getChar();

    // 문자입력받기
    std::string getString();

    void typingStart();

    int compare(const std::string& original, const std::string& input);


};


#endif // !__TYPING_SYSTEM_H__

1-4) User.h

#ifndef __USER_H__
#define __USER_H__

#include "Header.h"

class User {
public:
    //파일명
    std::string username;

    // 평균 타자 속도
    float avgSpeed = 0.0f;

    //타자 문장을 얼마나 입력했는지
    int typingCount = 0;

    User(const std::string& username);
    void save();
    void load();

    void printUserInfo();
};


#endif // !

2) 소스파일

2-1) Main.cpp

#include "Header.h"
#include "TypingSystem.h"

int main() {
    srand(time(0));
    TypingSystem* s = new TypingSystem();
    s->start();

    return 0;
}

2-2) TypingGameHelper.cpp

#include "TypingGameHelper.h"
#include <string>

void putStringOnPosition(int x, int y, const std::string& content) {
    // Move Cursor
    COORD pos;
    pos.X = x;
    pos.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
    // Print string
    printf("%s", content.c_str());
}

void showConsoleCursor(bool showFlag) {
    HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO     cursorInfo;
    GetConsoleCursorInfo(out, &cursorInfo);
    cursorInfo.bVisible = showFlag; // set the cursor visibility
    SetConsoleCursorInfo(out, &cursorInfo);
}

void clear() {
    system("cls");
}

2-3) TypingSystem.h

#include "TypingSystem.h"

void TypingSystem::loadData(){
    FILE* infile = fopen("sentences.txt", "r");

    for (;;) {
        // 파일의 끝에 도달하면 종료해라
        if (feof(infile) == 1) break;
        char line[512] = {0, };
        // null 문자를 위한 공간을 남겨 두는 것.
        fscanf(infile, "%511[^\n]s", line);
        fgetc(infile);
        sentences.push_back(line);
    }

    fclose(infile);

}

void TypingSystem::printData(){
    for (int i = 0; i < sentences.size(); i++) {
        printf("%s\n", sentences[i].c_str());
    }
}

void TypingSystem::start() {
    this->loadData();
    for (;;) {
        clear();
        printMenu();
        char input = getChar();

        if (input == '1' && currentUser == nullptr ) {

            putStringOnPosition(6, 4, "아이디를 입력해 주세요:");
            std::string username = getString();
            currentUser = new User(username);
            //로그인 진행
            //사용자로부터 입력을 받아서 해당 입력된 문자열을 통해
            // 문자열과 동일한 파일명이 있으면 그 파일에서 데이터를 읽어와서
            // 평균타수, 총 타자연습갯수를 불러오는 로직을 형성
        }
        else if (input == '1' && currentUser != nullptr) {
            //로그아웃 진행
            delete currentUser;
            currentUser = nullptr;
        }
        else if (input == '2' && currentUser != nullptr) {
            //타자연습 진행
            typingStart();

        }
        else if (input == '3' && currentUser != nullptr) {
            //유저 정보 출력
            currentUser->printUserInfo();
        }
        else if (input == 'x' || input == 'X') {
            //종료
            putStringOnPosition(6, 13, "프로그램을 종료 합니다.");
            break;
        }
        else {

        }

    }
}

void TypingSystem::printMenu(){
    if (currentUser == nullptr) {
        putStringOnPosition(6, 3, "1. 로그인");

    }
    else {
        putStringOnPosition(6, 3, "1. 로그아웃");
        putStringOnPosition(6, 4, "2. 타자연습 시작");
        putStringOnPosition(6, 5, "3. 연습 통계 보기");
    }


    putStringOnPosition(6, 12, "메뉴를 입력해 주세요(종료는 X 입력) : ");
}

char TypingSystem::getChar(){
    fseek(stdin, 0, SEEK_END);
    char c;
    scanf("%c", &c);
    return c;
}

std::string TypingSystem::getString(){
    char input[100];
    fseek(stdin, 0, SEEK_END);
    scanf("%99[^\n]s", input);

    return input;
}

void TypingSystem::typingStart(){
    float types_per_min = 0.0f;
    for (int i = 0; i < 5; i++) {
        clear();

        // sprintf를 위해서 만들어둔 배열
        char buff[128];

        //상단에 분당 타수를 출력 합니다.
        sprintf(buff, "분당 타수: %.2f / 평균타수 %.2f", types_per_min, currentUser->avgSpeed);
        putStringOnPosition(6, 5, buff);

        // 프로그램이 시작한지 몇초가 지난지 나옴.
        float start = (float)clock() / CLOCKS_PER_SEC;

        int target = rand() % sentences.size();
        sprintf(buff, "문장: %s", sentences[target].c_str());
        putStringOnPosition(6, 6, buff);

        //문자열을 사용자로부터 입력받는 곳
        putStringOnPosition(6, 7, "입력: ");
        std::string input = getString();

        float end = (float)clock() / CLOCKS_PER_SEC;

        // 시간이 얼마나 흘렀는지 계산
        float delta = end - start;

        // 분당 몇 타수 계산
        types_per_min = sentences[target].size() / delta * 60;

        int matches = compare(sentences[target], input);
        float match_ratio = (float)matches / sentences[target].size();

        types_per_min = types_per_min * match_ratio;

        // 타이핑 카운트 계산
        currentUser->typingCount++;
        currentUser->avgSpeed = (currentUser->avgSpeed * ((float)currentUser->typingCount - 1.0 ) + types_per_min) / (float)currentUser->typingCount ;



        // 입력된 문자열과, 해당 문자열의 일치율
        // 입력 속도를 계산 후 출력

    }
    currentUser->save();
    putStringOnPosition(6, 8, "연습이 종료되었습니다.");
    putStringOnPosition(6, 9, "계속하려면 엔터키를 눌러주세요: ");
    fseek(stdin, 0, SEEK_END);
    fgetc(stdin);
}

int TypingSystem::compare(const std::string& original, const std::string& input){
    int count = 0;

    int size = original.size() < input.size() ? original.size() : input.size();

    for (int i = 0; i < size; i++) {
        //at : 파라미터를 입력받아 파라미터 위치에 있는 문자를 리턴해줌.
        // original.c_str()[i] 와 같다.
        if (original.at(i) == input.at(i)) {
            count++;

        }
    }
    return count;
}

2-4) User.cpp

#include "User.h"

User::User(const std::string& username) {
    this->username = username;

    FILE* fp = fopen(username.c_str(), "r");
    bool exists = fp != nullptr;

    if (exists) fclose(fp);
    if (exists) {
        putStringOnPosition(6, 5, "사용자가 존재합니다... 불러오는중...");
        load();
        putStringOnPosition(6, 6, "계속하려면 엔터를 입력해주세요");
        fseek(stdin, 0, SEEK_END);
        fgetc(stdin);
    }
    else {
        putStringOnPosition(6, 5, "사용자가 존재하지 않습니다... 새로 만드는 중...");
        save();
        putStringOnPosition(6, 6, "계속하려면 엔터를 입력해주세요");
        fseek(stdin, 0, SEEK_END);
        fgetc(stdin);
    }

}

void User::save(){
    FILE* outfile = fopen(username.c_str(), "w");
    fprintf(outfile, "%f\n", avgSpeed);
    fprintf(outfile, "%d", typingCount);
    fclose(outfile);
}

void User::load(){
    FILE* infile = fopen(username.c_str(), "r");
    fscanf(infile, "%f", &avgSpeed);
    fgetc(infile);
    fscanf(infile, "%d", &typingCount);

    fclose(infile);
}

void User::printUserInfo(){
    char avgSpeed[50];

    //sprintf는 문자열에다 %f(포맷스트링) 를 집어넣을수 있음.
    sprintf(avgSpeed, "평균 타수: %f", this->avgSpeed);

    char count[50];
    sprintf(count, "타이핑 횟수 : %d", this->typingCount);

    putStringOnPosition(6, 6, avgSpeed);
    putStringOnPosition(6, 7, count);
    putStringOnPosition(6, 8, "계속 하려면 엔터키를 눌러 주세요");
    fseek(stdin, 0, SEEK_END);
    fgetc(stdin);

}
profile
코딩 일지

0개의 댓글