Giới hạn thời gian cho người dùng nhập, nếu hết thời gian thì thông báo hết giờ

Đây là 1 phần của chương trình mà em đang làm ạ. Em kiếm được một hàm giới hạn thời gian cho người dùng nhập trong 5 giây xong hết thời gian sẽ xuất ra 1 thông báo là hết giờ. Nhưng vấn đề mà em gặp ở đây là cái hàm này hình như nó chặn luôn cả cin ở chỗ lựa độ khó. Em nhập lựa chọn mà nó không nhận. Em thưr luôn cả bằng switch-case nhưng vẫn không được. Anh chị nào giúp cho em đi ạ. Em cảm ơn nhiều ạ.

#include <conio.h>
#include <iostream>
#include <windows.h>
#include<thread>

using namespace std;
void WaitFor5s() //Đây là hàm em cho người dùng 5s nhập đáp án mà em kiếm được ở trên mạng
{
string answer{};
	cout << "Nhap vao dap an trong vong 5 giay: ";
	

	time_t start = time(NULL);
	time_t waitTime = 5;

	//try to read a passsword in 5 seconds
	while (time(NULL) < start + waitTime && answer.empty())
	{
		thread t1([&]()
			{
				cin >> answer;

			});
		this_thread::sleep_for(chrono::milliseconds(200));
		t1.detach();
	}

	//check if password was entered
	if (answer.empty())
		cout << "\nOOPSSS HET GIO ROI !!!!!\n";
	else
		cout << "Dap an ban vua nhap la: " << answer << endl;
}
void main()
{
	char DoKho;
    do{
		cout << " lua do kho ";
		cin >> DoKho;// Vấn đề em gặp ở đây mỗi lần bắt đầu 1 vòng mới thì cin của em không nhận được lệnh ạ
		switch (DoKho) {
		case'D': WaitFor5s();
			cout << " gi do ";
			break;
		case'T': WaitFor5s();
			break;

	}
	
	} while (DoKho != 'X');

}

5 giây / 200 mili-giây = 25. Tức là ở hàm WaitFor5s bạn đã tạo ra xấp xỉ 25 luồng (thread) chờ nhập cin >> answer.
Bạn nghĩ sao nếu gọi detach? 25 luồng này sẽ chạy tự do và luôn ở trạng thái chờ nhập từ cin, nhận được thì luồng đó mới kết thúc.
Console của bạn đang có 25 thằng luôn canh chờ giật được bất kì thứ gì bạn nhập vào => luồng chính đứng nhìn.

1 Like

dạ em có thử xài join với giảm cái milisecond á anh. Nếu em xài join thì em sẽ nhập ở lựa chọn được;(( mà cái hàm 5s sẽ chờ đợi mãi mãi cho người dùng nhập. anh chỉ em cách giải quyết được không anh:(( em mày mò hơn cả 1 tuần rồi ạ

Một khi các lệnh nhập (input) được gọi thì phải chờ và luôn vậy, nếu muốn “ngừng nhập” thì chỉ có cách “giết” ứng dụng. Trên Windows thì có thể thoát khỏi chờ nhập bằng cách gọi đến hàm WriteConsoleOutput.

Còn đây là ví dụ nhập trước hay sau hời gian. Bắt buộc phải nhập!

1 Like

ko cần dùng thread cũng được, nhưng phải xài kbhit getch gì đấy trong windows.h :V

3 Likes

anh có thể hướng dẫn em cụ thể em hơn không ạ, em có coi về 2 cái hàm kbhit với getch() rồi ạ, 2 cái đều là nhận lệnh từ bàn phím mà em không rõ cách xài sao cho chuẩn ạ;<<

code cũ anh đang đọc lại :V

cách này hạn chế ở chỗ mình phải tự xử lý ký tự nhập vào, có thể là phím mũi tên hoặc nút HOME/END, backspace, del, v.v… rồi in ra tương ứng. Vì phải tự xóa dòng rồi in ra lại mỗi lần có keyboard input nên hàm timedLineInput phải hạn chế số lượng ký tự nhập vào để ko có xuống dòng trong console :V

#include <iostream>
#include <iomanip>
#include <string>
#include <chrono>
#include <conio.h>
#include <windows.h>

std::string timedLineInput(std::chrono::milliseconds waitDuration, size_t maxLen = 60, bool showTimer = true,
                           DWORD sleepDurationPerFrame = 30);

int main() {
    std::string s = timedLineInput(std::chrono::seconds(5), 10);
    std::cout << s << "\n";
}

std::string timedLineInput(std::chrono::milliseconds waitDuration, size_t maxLen, bool showTimer,
                           DWORD sleepDurationPerFrame) {
    std::string buff;
    size_t cursorIndex = 0;
    using namespace std::chrono;
    const auto deadline = steady_clock::now() + waitDuration;
    bool dirty = true;
    for (;; Sleep(sleepDurationPerFrame)) {
        if (steady_clock::now() >= deadline) { // time's up
            if (showTimer) std::cout << "\r" << std::fixed << std::setprecision(2) << std::setw(5) << 0.0;
            break;
        }
        if (showTimer) dirty = true;
        if (_kbhit()) {
            dirty = true;
            int ch = _getch();
            if (ch == '\r' || ch == '\n') break; // break at \r or \n
            if (ch == '\b') {                    // backspace
                if (!buff.empty()) {             // there is character to delete
                    buff.erase(--cursorIndex, 1);
                    printf("\b \b"); // delete last character on screen (a hack)
                }
            } else if (ch == 0xE0) { // DEL, arrow keys, ... emits 2 characters 0xE0
                ch = _getch();       // and this character
                if (ch == 0x53) {    // DEL
                    if (!buff.empty()) {
                        if (cursorIndex == buff.size()) {
                            buff.erase(--cursorIndex);
                            printf("\b \b");
                        } else {
                            buff.erase(cursorIndex, 1);
                        }
                    }
                } else if (ch == 0x4b) { // left arrow
                    if (cursorIndex > 0) cursorIndex--;
                } else if (ch == 0x4d) { // right arrow
                    if (cursorIndex < buff.size()) cursorIndex++;
                } else if (ch == 0x47) { // Home
                    cursorIndex = 0;
                } else if (ch == 0x4f) { // End
                    cursorIndex = buff.size();
                }
            } else if (buff.size() < maxLen) {
                buff.insert(cursorIndex++, 1, ch);
            }
        }
        if (dirty) {
            std::cout << "\r" << std::string(10 + maxLen, ' ') << '\r'; // clear line
            if (showTimer) {
                const duration<double> timeLeft = deadline - steady_clock::now();
                std::cout << std::fixed << std::setprecision(2) << std::setw(5) << timeLeft.count() << "s    ";
            }
            std::cout << buff;
            std::cout << std::string(buff.size() - cursorIndex, '\b');
            std::cout << std::flush;
        }
        dirty = false;
    }
    std::cout << "\n";
    return buff;
}

edit: code cũ cũ nữa ko xài <chrono>:

#include <conio.h>
#include <windows.h>
#include <string>
#include <iostream>
#include <cstdio>

///////////////////////////////////////////////////////////////////////
/// \brief Input with time limit.
/// Prevent user from inserting more input after time limit has been spent.
/// \param timeLeft Time limit, in milliseconds.
/// \param maxLen Maximum input length (to prevent long input that
/// causes a new line).
/// \return Input string.
///////////////////////////////////////////////////////////////////////
std::string timedLineInput(unsigned int timeLeft, size_t maxLen = 40);

int main()
{
    std::string s = timedLineInput(11000);
    std::cout << s << "\n";
}

std::string timedLineInput(unsigned int timeLeft, size_t maxLen)
{
    std::string buff;
    size_t cursorIndex = 0;
    DWORD deadline = GetTickCount() + timeLeft;
    while (1)
    {
        DWORD now = GetTickCount();
        timeLeft = deadline < now ? 0 : deadline - now;
        if (!timeLeft) break;
        if (kbhit())
        {
            int ch = getch();
            if (ch == '\r' || ch == '\n') break;
            if (ch == '\b') //backspace
            {
                if (!buff.empty()) //there is character to delete
                {
                    buff.erase(--cursorIndex, 1);
                    printf("\b \b"); //delete last character on screen (a hack)
                }
            }
            else if (ch == 0xE0) //DEL, arrow keys, ... emits 2 characters 0xE0
            {
                ch = getch();    //and this character
                if (ch == 0x53) //DEL
                {
                    if (!buff.empty())
                    {
                        if (cursorIndex == buff.size())
                        {
                            buff.erase(--cursorIndex);
                            printf("\b \b");
                        }
                        else
                        {
                            buff.erase(cursorIndex, 1);
                        }
                    }
                }
                else if (ch == 0x4b) //left arrow
                {
                    if (cursorIndex > 0) cursorIndex--;
                }
                else if (ch == 0x4d) //right arrow
                {
                    if (cursorIndex < buff.size()) cursorIndex++;
                }
                else if (ch == 0x47) //Home
                {
                    cursorIndex = 0;
                }
                else if (ch == 0x4f) //End
                {
                    cursorIndex = buff.size();
                }
            }
            else if (buff.size() < maxLen)
            {
                buff.insert(cursorIndex++, 1, ch);
            }
        }
        printf("\r%79s", ""); //clear line
        //5 is magic number for time limit < 100s.
        //For 100s < time limit < 1000s use 6, or convert to minutes...
        printf("\r%5.2fs    %s", timeLeft / 1000.0, buff.c_str());
        //set cursor position without gotoxy...
        printf("\r%5.2fs    %s", timeLeft / 1000.0, buff.substr(0, cursorIndex).c_str());
        Sleep(64); //decrease this number until there is no blinking on screen
    }
    printf("\r%5.2fs    %s\n", timeLeft / 1000.0, buff.c_str());
    return buff;
}

xài <chrono> đẹp hơn ở chỗ nhập vào seconds hay milliseconds gì nó cũng rõ ràng =]

4 Likes

anh đỉnh quá em cảm ơn anh ạ

1 Like
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?