From be9c07a12a8fe52c044d642bb4b535104ab6482a Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Sun, 21 Nov 2021 13:49:23 +0200 Subject: [PATCH 01/14] TASK1: .gitignore added --- 01_git/.gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 01_git/.gitignore diff --git a/01_git/.gitignore b/01_git/.gitignore new file mode 100644 index 0000000..92e163a --- /dev/null +++ b/01_git/.gitignore @@ -0,0 +1,3 @@ +*.out +*.json +game From 79a25068a255aac6b6d5c05f26d87ea8a928f8f4 Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Sun, 21 Nov 2021 14:45:38 +0200 Subject: [PATCH 02/14] TASK1: Makefile and game sources added. --- 01_git/scissors/Makefile | 12 ++++ 01_git/scissors/game.cpp | 149 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 01_git/scissors/Makefile create mode 100644 01_git/scissors/game.cpp diff --git a/01_git/scissors/Makefile b/01_git/scissors/Makefile new file mode 100644 index 0000000..47e0887 --- /dev/null +++ b/01_git/scissors/Makefile @@ -0,0 +1,12 @@ +CXX=g++ +CXX_FLAGS=-std=c++11 -O3 -Wall -Werror +CXX_SOURCES=game.cpp +OUT_NAME=game + +game: + $(CXX) $(CXX_FLAGS) $(CXX_SOURCES) -o$(OUT_NAME) + +all: game + +clean: + rm $(OUT_NAME) diff --git a/01_git/scissors/game.cpp b/01_git/scissors/game.cpp new file mode 100644 index 0000000..b661924 --- /dev/null +++ b/01_git/scissors/game.cpp @@ -0,0 +1,149 @@ +#include +#include + +// Not using enum class since too lazy to +// do explicit casts to size_t everywhere. +enum Choice +{ + SCISSORS = 0, + PAPER, + ROCK, + CHOICE_LAST +}; + +enum GameResult +{ + PLAYER_WINS = 0, + CPU_WINS, + DRAW, + GR_LAST +}; + +namespace +{ + +const char* gaItemsStrRepr[] +{ + "scissors", // SCISSORS + "paper", // PAPER + "rock" // ROCK +}; + +const char* gaGameResStrRepr[] +{ + "You win", // PLAYER_WINS + "I win", // CPU_WINS + "Draw!" // DRAW +}; + +GameResult gaResolveTbl[][CHOICE_LAST] { + // SCISSORS | PAPER | ROCK <- Player's choice + // + // CPU's choice + // | + // V + { DRAW, PLAYER_WINS, CPU_WINS }, // SCISSORS + { CPU_WINS, DRAW, PLAYER_WINS }, // PAPER + { PLAYER_WINS, CPU_WINS, DRAW } // ROCK +}; + +void resolveGame(Choice rePlayerChoice, Choice reCpuChoice) +{ + const auto eResult = gaResolveTbl[rePlayerChoice][reCpuChoice]; + std::cout << gaGameResStrRepr[eResult]; + + if (eResult != DRAW) + { + const auto eFirstNoun = (eResult == CPU_WINS) ? reCpuChoice : rePlayerChoice; + const auto eSecondNoun = (eFirstNoun == reCpuChoice) ? rePlayerChoice : reCpuChoice; + std::cout << ": " << gaItemsStrRepr[eFirstNoun]; + std::cout << " beat" << (eFirstNoun != SCISSORS ? "s" : ""); + std::cout << " " << gaItemsStrRepr[eSecondNoun]; + } + + std::cout << std::endl; +} + +Choice decodeChoice(int rdRawChoice) +{ + switch (rdRawChoice) + { + case 's': + return SCISSORS; + case 'p': + return PAPER; + case 'r': + return ROCK; + default: + return CHOICE_LAST; + } +} + +Choice makeCpuChoice() +{ + while (true) + { + const auto dRand = rand() % CHOICE_LAST; + if (dRand < CHOICE_LAST) + { + return Choice(dRand); + } + } +} +} // namespace + +int main() +{ + std::srand(time(nullptr)); + + int cInput = 0; + bool bSkipHeading = false; + + while (true) + { + if (not bSkipHeading) + { + std::cout << "Please choose: rock (r) - paper (p) - scissors (s)" << std::endl; + } + else + { + bSkipHeading = false; + } + + cInput = std::tolower(std::getchar()); + if (cInput == 'q' or cInput == EOF) + { + break; + } + + // Eat up LFs and CRs, do not repeat the prompt above. + if (cInput == '\n' or cInput == '\r') + { + bSkipHeading = true; + continue; + } + + const auto ePlayerChoice = decodeChoice(cInput); + if (ePlayerChoice == CHOICE_LAST) + { + std::cout << "Wrong input"; + if (std::isalnum(cInput)) + { + // Check letters & numbers only, do not overcomplicate things. + std::cout << ": " << char(cInput); + } + + std::cout << "!" << std::endl << "Press 'q' or '^D' to exit" << std::endl; + continue; + } + + // Make CPU's move. + const auto eCpuChoice = makeCpuChoice(); + + std::cout << "You chose " << gaItemsStrRepr[ePlayerChoice]; + std::cout << ", I chose " << gaItemsStrRepr[eCpuChoice] << std::endl; + resolveGame(ePlayerChoice, eCpuChoice); + } + + std::cout << "Bye!" << std::endl; +} From 10d014d3432e0d2f9ff75db7ff49d452b2282b07 Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Sun, 21 Nov 2021 14:55:45 +0200 Subject: [PATCH 03/14] TASK1: Minor style fixes - Add const-ness where appropriate; - Make CPU move before the player's one. --- 01_git/scissors/game.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/01_git/scissors/game.cpp b/01_git/scissors/game.cpp index b661924..19d2e13 100644 --- a/01_git/scissors/game.cpp +++ b/01_git/scissors/game.cpp @@ -36,7 +36,8 @@ const char* gaGameResStrRepr[] "Draw!" // DRAW }; -GameResult gaResolveTbl[][CHOICE_LAST] { +const GameResult gaResolveTbl[][CHOICE_LAST] +{ // SCISSORS | PAPER | ROCK <- Player's choice // // CPU's choice @@ -123,6 +124,9 @@ int main() continue; } + // Make CPU's move first, we need to play fair. + const auto eCpuChoice = makeCpuChoice(); + const auto ePlayerChoice = decodeChoice(cInput); if (ePlayerChoice == CHOICE_LAST) { @@ -137,9 +141,6 @@ int main() continue; } - // Make CPU's move. - const auto eCpuChoice = makeCpuChoice(); - std::cout << "You chose " << gaItemsStrRepr[ePlayerChoice]; std::cout << ", I chose " << gaItemsStrRepr[eCpuChoice] << std::endl; resolveGame(ePlayerChoice, eCpuChoice); From 9ce3e0db2bb42be63c162e269ee14c16cfd51010 Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Sun, 21 Nov 2021 16:18:33 +0200 Subject: [PATCH 04/14] TASK1: MD5 output added, minor Makefile fixes. --- 01_git/scissors/Makefile | 15 +++++++++++---- 01_git/scissors/game.cpp | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/01_git/scissors/Makefile b/01_git/scissors/Makefile index 47e0887..4e247e0 100644 --- a/01_git/scissors/Makefile +++ b/01_git/scissors/Makefile @@ -1,12 +1,19 @@ CXX=g++ -CXX_FLAGS=-std=c++11 -O3 -Wall -Werror +CXX_FLAGS=-std=c++11 -O3 -Wall -Werror -Wpedantic +LDFLAGS=-lcrypto + CXX_SOURCES=game.cpp OUT_NAME=game -game: - $(CXX) $(CXX_FLAGS) $(CXX_SOURCES) -o$(OUT_NAME) +RM=rm -rf + +# The project is tiny so do clean build at all times +# to suppress $TARGET is up to date" in case we're +# updating the module. +game: clean + $(CXX) $(CXX_FLAGS) $(CXX_SOURCES) -o$@ $(LDFLAGS) all: game clean: - rm $(OUT_NAME) + $(RM) $(OUT_NAME) diff --git a/01_git/scissors/game.cpp b/01_git/scissors/game.cpp index 19d2e13..a73aad5 100644 --- a/01_git/scissors/game.cpp +++ b/01_git/scissors/game.cpp @@ -1,6 +1,9 @@ #include +#include #include +#include + // Not using enum class since too lazy to // do explicit casts to size_t everywhere. enum Choice @@ -80,6 +83,33 @@ Choice decodeChoice(int rdRawChoice) } } +void getAndPrintMd5Sum(const char* rpChoiceString) +{ + if (rpChoiceString == nullptr) + { + perror("Null CPU choice input!"); + return; + } + + const auto dChoiceLength = std::strlen(rpChoiceString); + if (dChoiceLength == 0) + { + perror("Empty input."); + return; + } + + unsigned char aDigest[MD5_DIGEST_LENGTH] {}; + const auto pDigest = MD5(reinterpret_cast(rpChoiceString), + dChoiceLength, aDigest); + std::cout << "MD5(\"" << rpChoiceString << "\"): 0x"; + for (size_t i = 0; i < sizeof(aDigest); ++i) + { + std::cout << std::hex << short(pDigest[i]); + } + + std::cout << std::endl; +} + Choice makeCpuChoice() { while (true) @@ -87,7 +117,9 @@ Choice makeCpuChoice() const auto dRand = rand() % CHOICE_LAST; if (dRand < CHOICE_LAST) { - return Choice(dRand); + const auto eChoice = Choice(dRand); + getAndPrintMd5Sum(gaItemsStrRepr[eChoice]); + return eChoice; } } } From 0500e438c0d4685eb8cc5f6e0251d0c0e79ece57 Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Mon, 22 Nov 2021 11:41:17 +0200 Subject: [PATCH 05/14] TASK2: Add hwdetect device nodes monitoring script --- 02_bash/hwdetect/hwdetect.sh | 85 ++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100755 02_bash/hwdetect/hwdetect.sh diff --git a/02_bash/hwdetect/hwdetect.sh b/02_bash/hwdetect/hwdetect.sh new file mode 100755 index 0000000..65bd0d3 --- /dev/null +++ b/02_bash/hwdetect/hwdetect.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# PLEASE NOTE: the script works with bash only! +# Other shells, like zsh are not supported. + +# Add other subsystems which can be tested with hotplugging to monitor. +# Currently we listen to addition/removal of: +# 1) USB pendrives/SD/MMC cards: mmc and block +# 2) USB-HID devices (and generic HID devices, like PS/2? I tested with yubikey & mice only): hid +# 3) USB-to-UART converters: usb-setial (also tty may be added) +monitored_subsys=(block hid usb-serial mmc) + +temp_buf=`mktemp` + +for subsys in "${monitored_subsys[@]}" +do + query+="--subsystem-match=$subsys " +done + +udevadm monitor -k $query > $temp_buf & +udevadm_pid=$! + +cleanup () { + kill -SIGTERM $udevadm_pid + rm -rf $temp_buf +} + +filter() { + # tail + grep combo return a single block of strings separated with + # spaces instead of '\n's. udevadm does not have an ability to filter + # events by action types by itself so, for example inserting a new + # USB device generates lots of output which contains also 'bind' actions + # wehich we consider just a noise for this task. These must be excluded + # from the output, so we iterate through the list of words in search + # for the give tag (like 'add' or 'remove'); in case one is found + # the next token is device name (according to udevadm format). Sub-classes + # are filtered by --subsystem-match so we don't need to filter that lines. + + local tag=$1 + local tag_found=0 + local input=("$@") + + # Iterate from the first arg (not zero-th!) + # this is a crutch to suppress garbage in output + for line in "${input[@]:1}"; + do + if [ $tag_found -eq 0 ]; + then + if [ "$tag" == "$line" ]; + then + tag_found=1 + fi + else + echo $line + tag_found=0 + fi + done +} + +trap cleanup EXIT SIGTERM + +read_pos=0 + +while : ; +do + diff_list=`tail -c +$read_pos $temp_buf` + diff_len=${#diff_list} + ((read_pos+=$diff_len)) + added_list=`echo $diff_list | grep add` + removed_list=`echo $diff_list | grep remove` + + if [ ! -z "${added_list}" ]; then + echo + echo Added devices: + filter "add" $added_list + fi + + if [ ! -z "${removed_list}" ]; then + echo + echo Removed devices: + filter "remove" $removed_list + fi + + sleep 1 +done From 7eb27c2a6a72f0eb843908f0ffb027e14128d530 Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Mon, 22 Nov 2021 11:47:59 +0200 Subject: [PATCH 06/14] TASK2: Fix a typo in the header comment --- 02_bash/hwdetect/hwdetect.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/02_bash/hwdetect/hwdetect.sh b/02_bash/hwdetect/hwdetect.sh index 65bd0d3..cc363f1 100755 --- a/02_bash/hwdetect/hwdetect.sh +++ b/02_bash/hwdetect/hwdetect.sh @@ -7,7 +7,7 @@ # Currently we listen to addition/removal of: # 1) USB pendrives/SD/MMC cards: mmc and block # 2) USB-HID devices (and generic HID devices, like PS/2? I tested with yubikey & mice only): hid -# 3) USB-to-UART converters: usb-setial (also tty may be added) +# 3) USB-to-UART converters: usb-serial (also tty may be added) monitored_subsys=(block hid usb-serial mmc) temp_buf=`mktemp` From 93d1363347039b5a7933bfadb4d73e9d7240e446 Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Mon, 22 Nov 2021 12:56:35 +0200 Subject: [PATCH 07/14] TASK2: Fix more typos --- 02_bash/hwdetect/hwdetect.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/02_bash/hwdetect/hwdetect.sh b/02_bash/hwdetect/hwdetect.sh index cc363f1..e5c84ab 100755 --- a/02_bash/hwdetect/hwdetect.sh +++ b/02_bash/hwdetect/hwdetect.sh @@ -26,15 +26,17 @@ cleanup () { } filter() { - # tail + grep combo return a single block of strings separated with + # tail + grep combo returns a single block of strings separated with # spaces instead of '\n's. udevadm does not have an ability to filter - # events by action types by itself so, for example inserting a new - # USB device generates lots of output which contains also 'bind' actions - # wehich we consider just a noise for this task. These must be excluded + # events by action types by itself so, for example inserting/removing a + # USB device generates lots of output which contain also 'bind/unbind' actions + # which we consider just a noise for this task. These must be excluded # from the output, so we iterate through the list of words in search - # for the give tag (like 'add' or 'remove'); in case one is found - # the next token is device name (according to udevadm format). Sub-classes + # for the given tag (like 'add' or 'remove'); in case one is found + # the next token is the device name (according to udevadm format). Sub-classes # are filtered by --subsystem-match so we don't need to filter that lines. + # This also eats up [KERNEL... headers. + # The same could be probably achieved by awk? local tag=$1 local tag_found=0 @@ -66,6 +68,10 @@ do diff_list=`tail -c +$read_pos $temp_buf` diff_len=${#diff_list} ((read_pos+=$diff_len)) + + # Preprocess add/remove lists in order not to + # filter empty input or an input which does not + # contain data which we're interested in. added_list=`echo $diff_list | grep add` removed_list=`echo $diff_list | grep remove` From 9386c25fe5c1d0a8558a0c6a08b31b4beaad58c6 Mon Sep 17 00:00:00 2001 From: Artem Komyshan Date: Sun, 21 Nov 2021 18:19:45 +0200 Subject: [PATCH 08/14] Bash HW: create hot plugged hardware detector --- 02_bash/hwdetect.sh | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100755 02_bash/hwdetect.sh diff --git a/02_bash/hwdetect.sh b/02_bash/hwdetect.sh new file mode 100755 index 0000000..a50a121 --- /dev/null +++ b/02_bash/hwdetect.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +allDevices=(ttyUSB i2c sd mmcblk) + +function checkDevicesDiff() { + device=${1} + ls /dev | grep ${device} > ${device}_updated.txt + updated_dev=$(diff ${device}.txt ${device}_updated.txt) + + if [[ -n ${updated_dev} ]]; then + echo "Devices status changed ${device}: ${updated_dev}" + cp ${device}_updated.txt ${device}.txt + fi +} + +for device in ${allDevices[@]}; do + ls /dev | grep ${device} > ${device}.txt + + if [[ -s ${device}.txt ]]; then + cat ${device}.txt + else + echo "No ${device} devices detected" + fi +done + +echo "\">\" - means connected device" +echo "\"<\" - means disconnected device" + +while(true); do + sleep 1 + for device in ${allDevices[@]}; do + checkDevicesDiff "${device}" + done +done + +exit 0 + From c0da3e3444303851db935de0a5b61d69f134e446 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 23 Nov 2021 14:07:33 +0200 Subject: [PATCH 09/14] Revert "Task2: Bash HW create hot plugged hardware detector" --- 02_bash/hwdetect.sh | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100755 02_bash/hwdetect.sh diff --git a/02_bash/hwdetect.sh b/02_bash/hwdetect.sh deleted file mode 100755 index a50a121..0000000 --- a/02_bash/hwdetect.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -allDevices=(ttyUSB i2c sd mmcblk) - -function checkDevicesDiff() { - device=${1} - ls /dev | grep ${device} > ${device}_updated.txt - updated_dev=$(diff ${device}.txt ${device}_updated.txt) - - if [[ -n ${updated_dev} ]]; then - echo "Devices status changed ${device}: ${updated_dev}" - cp ${device}_updated.txt ${device}.txt - fi -} - -for device in ${allDevices[@]}; do - ls /dev | grep ${device} > ${device}.txt - - if [[ -s ${device}.txt ]]; then - cat ${device}.txt - else - echo "No ${device} devices detected" - fi -done - -echo "\">\" - means connected device" -echo "\"<\" - means disconnected device" - -while(true); do - sleep 1 - for device in ${allDevices[@]}; do - checkDevicesDiff "${device}" - done -done - -exit 0 - From 4a9e2f5b1585533f13c74c0c590d57d2ed75a050 Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Tue, 23 Nov 2021 17:29:45 +0200 Subject: [PATCH 10/14] TASK1: Project structure refactoring --- 01_git/scissors/Makefile | 5 +- 01_git/scissors/choice.cpp | 150 ++++++++++++++++++++++++++++++++ 01_git/scissors/choice.h | 69 +++++++++++++++ 01_git/scissors/game.cpp | 171 ++++++++----------------------------- 4 files changed, 256 insertions(+), 139 deletions(-) create mode 100644 01_git/scissors/choice.cpp create mode 100644 01_git/scissors/choice.h diff --git a/01_git/scissors/Makefile b/01_git/scissors/Makefile index 4e247e0..5631a8f 100644 --- a/01_git/scissors/Makefile +++ b/01_git/scissors/Makefile @@ -1,8 +1,9 @@ CXX=g++ CXX_FLAGS=-std=c++11 -O3 -Wall -Werror -Wpedantic +INC=-I. LDFLAGS=-lcrypto -CXX_SOURCES=game.cpp +CXX_SOURCES=choice.cpp game.cpp OUT_NAME=game RM=rm -rf @@ -11,7 +12,7 @@ RM=rm -rf # to suppress $TARGET is up to date" in case we're # updating the module. game: clean - $(CXX) $(CXX_FLAGS) $(CXX_SOURCES) -o$@ $(LDFLAGS) + $(CXX) $(CXX_FLAGS) $(INC) $(CXX_SOURCES) -o$@ $(LDFLAGS) all: game diff --git a/01_git/scissors/choice.cpp b/01_git/scissors/choice.cpp new file mode 100644 index 0000000..1e28b80 --- /dev/null +++ b/01_git/scissors/choice.cpp @@ -0,0 +1,150 @@ +#include "choice.h" + +#include +#include +#include + +#include + +namespace +{ + +const char* const gaItemsStrRepr[] +{ + "scissors", // SCISSORS + "paper", // PAPER + "rock" // ROCK +}; + +} //namespace + +namespace game +{ +// Auxiliary stuff. + +CVal decodeChoiceStdin(char rdRawChoice) +{ + switch (std::tolower(rdRawChoice)) + { + // In-game items. + case 's': + return CVal::SCISSORS; + case 'p': + return CVal::PAPER; + case 'r': + return CVal::ROCK; + + // Control codes. + case 'q': + // FALLTHROUGH + case EOF: + // FALLTHROUGH + case 0: // ^D - a corner-case for std::cin! In case ^D is pressed + // we get into an infinite loop. + return CVal::QUIT; + case '\n': + // FALLTHROUGH + case '\r': + return CVal::IGNORE; + + default: + return CVal::CHOICE_LAST; + } +} + +CVal decodeChoiceInt(int rdRawChoice) +{ + for (auto i = Choice::ToInt(CVal::SCISSORS); + i < Choice::ToInt(CVal::CHOICE_LAST); ++i) + { + if (i == rdRawChoice) + { + return static_cast(i); + } + } + + return CVal::CHOICE_LAST; +} + +// Choice class impl. + +const char* const Choice::ToString(CVal reValue) +{ + assert(reValue < CVal::CHOICE_LAST); + return gaItemsStrRepr[ToInt(reValue)]; +} + +void Choice::PrintMd5FromChoice(CVal reValue) +{ + assert(reValue < CVal::CHOICE_LAST); + const auto pChoiceString = ToString(reValue); + const auto dChoiceLength = std::strlen(pChoiceString); + + unsigned char aDigest[MD5_DIGEST_LENGTH] {}; + const auto pDigest = MD5(reinterpret_cast(pChoiceString), + dChoiceLength, aDigest); + std::cout << "MD5(choice): 0x"; + for (size_t i = 0; i < sizeof(aDigest); ++i) + { + std::cout << std::hex << short(pDigest[i]); + } + + std::cout << std::endl; +} + +// ChoiceStdin class impl. + +CVal ChoiceStdin::Get() +{ + bool bSkipHeading = false; + + while (true) + { + if (not bSkipHeading) + { + std::cout << "Please choose: rock (r) - paper (p) - scissors (s)" << std::endl; + } + else + { + bSkipHeading = false; + } + + auto dInput = '\0'; + std::cin >> dInput; + const auto eChoice {decodeChoiceStdin(dInput)}; + + if (eChoice == CVal::IGNORE) + { + // Eat up LFs and CRs, do not repeat the prompt above. + bSkipHeading = true; + continue; + } + + if (eChoice == CVal::CHOICE_LAST) + { + std::cout << "Wrong input"; + if (std::isalnum(dInput)) + { + // Check letters & numbers only, do not overcomplicate things. + std::cout << ": " << char(dInput); + } + + std::cout << "!" << std::endl << "Press 'q' or '^C'/'^D' to exit" << std::endl; + continue; + } + + return eChoice; + } +} + +// ChoiceRandom class impl. + +CVal ChoiceRandom::Get() +{ + const auto eChoice = decodeChoiceInt(mtUniDistr(mtRandSrc)); + assert(eChoice != CVal::CHOICE_LAST); // Double-checking UniDistr range. + PrintMd5FromChoice(eChoice); + return eChoice; +} + +} // namespace game diff --git a/01_git/scissors/choice.h b/01_git/scissors/choice.h new file mode 100644 index 0000000..ae332c9 --- /dev/null +++ b/01_git/scissors/choice.h @@ -0,0 +1,69 @@ +#pragma once + +#include + +namespace game +{ + +class Choice +{ +public: + + enum class Value : int + { + // Game-related values. + SCISSORS = 0, + PAPER, + ROCK, + + // Enum size & invalid input marker. + CHOICE_LAST, + // Crutch to skip some control chars, like CR & LF + IGNORE, + // "game quit" choice. + QUIT + }; + + virtual Value Get() = 0; + + static constexpr int ToInt(Value reValue) + { + return static_cast(reValue); + } + + static const char* const ToString(Value reValue); + static void PrintMd5FromChoice(Value reValue); + +}; + +using CVal = Choice::Value; + +class ChoiceStdin : public Choice +{ +public: + + Value Get() override; +}; + +class ChoiceRandom : public Choice +{ + using UniDistr = std::uniform_int_distribution<>; + +public: + + ChoiceRandom() : + mtUniDistr(ToInt(CVal::SCISSORS), ToInt(CVal::ROCK)) + { + std::random_device tDrbg; + mtRandSrc.seed(tDrbg()); + } + + Value Get() override; + +private: + + std::mt19937 mtRandSrc; + UniDistr mtUniDistr; +}; + +} // namespace game diff --git a/01_git/scissors/game.cpp b/01_git/scissors/game.cpp index a73aad5..4fbc1a1 100644 --- a/01_git/scissors/game.cpp +++ b/01_git/scissors/game.cpp @@ -1,180 +1,77 @@ -#include -#include -#include - -#include +#include "choice.h" -// Not using enum class since too lazy to -// do explicit casts to size_t everywhere. -enum Choice -{ - SCISSORS = 0, - PAPER, - ROCK, - CHOICE_LAST -}; +#include -enum GameResult +enum class Result : int { PLAYER_WINS = 0, CPU_WINS, - DRAW, - GR_LAST + DRAW }; -namespace -{ +using namespace game; -const char* gaItemsStrRepr[] +namespace { - "scissors", // SCISSORS - "paper", // PAPER - "rock" // ROCK -}; -const char* gaGameResStrRepr[] +const char* const gaGameResStrRepr[] { "You win", // PLAYER_WINS "I win", // CPU_WINS "Draw!" // DRAW }; -const GameResult gaResolveTbl[][CHOICE_LAST] +const Result gaResolveTbl[][Choice::ToInt(CVal::CHOICE_LAST)] { - // SCISSORS | PAPER | ROCK <- Player's choice + // SCISSORS | PAPER | ROCK <- Player's choice // - // CPU's choice - // | - // V - { DRAW, PLAYER_WINS, CPU_WINS }, // SCISSORS - { CPU_WINS, DRAW, PLAYER_WINS }, // PAPER - { PLAYER_WINS, CPU_WINS, DRAW } // ROCK + // CPU's choice + // | + // V + { Result::DRAW, Result::PLAYER_WINS, Result::CPU_WINS }, // SCISSORS + { Result::CPU_WINS, Result::DRAW, Result::PLAYER_WINS }, // PAPER + { Result::PLAYER_WINS, Result::CPU_WINS, Result::DRAW } // ROCK }; -void resolveGame(Choice rePlayerChoice, Choice reCpuChoice) -{ - const auto eResult = gaResolveTbl[rePlayerChoice][reCpuChoice]; - std::cout << gaGameResStrRepr[eResult]; - - if (eResult != DRAW) - { - const auto eFirstNoun = (eResult == CPU_WINS) ? reCpuChoice : rePlayerChoice; - const auto eSecondNoun = (eFirstNoun == reCpuChoice) ? rePlayerChoice : reCpuChoice; - std::cout << ": " << gaItemsStrRepr[eFirstNoun]; - std::cout << " beat" << (eFirstNoun != SCISSORS ? "s" : ""); - std::cout << " " << gaItemsStrRepr[eSecondNoun]; - } - - std::cout << std::endl; -} - -Choice decodeChoice(int rdRawChoice) +void resolveGame(CVal rePlayerChoice, CVal reCpuChoice) { - switch (rdRawChoice) - { - case 's': - return SCISSORS; - case 'p': - return PAPER; - case 'r': - return ROCK; - default: - return CHOICE_LAST; - } -} + const auto eResult = gaResolveTbl + [Choice::ToInt(rePlayerChoice)] + [Choice::ToInt(reCpuChoice)]; -void getAndPrintMd5Sum(const char* rpChoiceString) -{ - if (rpChoiceString == nullptr) - { - perror("Null CPU choice input!"); - return; - } + std::cout << gaGameResStrRepr[static_cast(eResult)]; - const auto dChoiceLength = std::strlen(rpChoiceString); - if (dChoiceLength == 0) + if (eResult != Result::DRAW) { - perror("Empty input."); - return; - } - - unsigned char aDigest[MD5_DIGEST_LENGTH] {}; - const auto pDigest = MD5(reinterpret_cast(rpChoiceString), - dChoiceLength, aDigest); - std::cout << "MD5(\"" << rpChoiceString << "\"): 0x"; - for (size_t i = 0; i < sizeof(aDigest); ++i) - { - std::cout << std::hex << short(pDigest[i]); + const auto eFirstNoun = (eResult == Result::CPU_WINS) ? reCpuChoice : rePlayerChoice; + const auto eSecondNoun = (eFirstNoun == reCpuChoice) ? rePlayerChoice : reCpuChoice; + std::cout << ": " << Choice::ToString(eFirstNoun); + std::cout << " beat" << (eFirstNoun != CVal::SCISSORS ? "s" : ""); + std::cout << " " << Choice::ToString(eSecondNoun); } std::cout << std::endl; } - -Choice makeCpuChoice() -{ - while (true) - { - const auto dRand = rand() % CHOICE_LAST; - if (dRand < CHOICE_LAST) - { - const auto eChoice = Choice(dRand); - getAndPrintMd5Sum(gaItemsStrRepr[eChoice]); - return eChoice; - } - } -} } // namespace int main() { - std::srand(time(nullptr)); - - int cInput = 0; - bool bSkipHeading = false; + ChoiceStdin tPlayerInput; + ChoiceRandom tCpuInput; while (true) { - if (not bSkipHeading) - { - std::cout << "Please choose: rock (r) - paper (p) - scissors (s)" << std::endl; - } - else - { - bSkipHeading = false; - } - - cInput = std::tolower(std::getchar()); - if (cInput == 'q' or cInput == EOF) - { - break; - } - - // Eat up LFs and CRs, do not repeat the prompt above. - if (cInput == '\n' or cInput == '\r') - { - bSkipHeading = true; - continue; - } // Make CPU's move first, we need to play fair. - const auto eCpuChoice = makeCpuChoice(); - - const auto ePlayerChoice = decodeChoice(cInput); - if (ePlayerChoice == CHOICE_LAST) + const auto eCpuChoice = tCpuInput.Get(); + const auto ePlayerChoice = tPlayerInput.Get(); + if (ePlayerChoice == CVal::QUIT) { - std::cout << "Wrong input"; - if (std::isalnum(cInput)) - { - // Check letters & numbers only, do not overcomplicate things. - std::cout << ": " << char(cInput); - } - - std::cout << "!" << std::endl << "Press 'q' or '^D' to exit" << std::endl; - continue; + break; } - std::cout << "You chose " << gaItemsStrRepr[ePlayerChoice]; - std::cout << ", I chose " << gaItemsStrRepr[eCpuChoice] << std::endl; + std::cout << "You chose " << Choice::ToString(ePlayerChoice); + std::cout << ", I chose " << Choice::ToString(eCpuChoice) << std::endl; resolveGame(ePlayerChoice, eCpuChoice); } From 4f320db9de8a9edc4d3939b7bea58355ac7f35d4 Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Sun, 28 Nov 2021 21:41:36 +0200 Subject: [PATCH 11/14] TASK3: Implement a basic module with params. Implement a simple module which takes two optional int parameters upon loading: first_arg and second_arg and computes their sum in the initialization routine and difference in the exit routine. In case one of the params (or both) is missing its default value is 0. Usage: insmod argsmod.ko [first_arg=X] [second_arg=Y] Signed-off-by: Victor Krasnoshchok --- 03_module/Makefile | 17 +++++++++++++++++ 03_module/argsmod.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 03_module/Makefile create mode 100644 03_module/argsmod.c diff --git a/03_module/Makefile b/03_module/Makefile new file mode 100644 index 0000000..7337fb9 --- /dev/null +++ b/03_module/Makefile @@ -0,0 +1,17 @@ +KDIR ?= ~/work/buildroot-2021.02.7/output/build/linux-5.10.7 +CHECKPATCH := $(KDIR)/scripts/checkpatch.pl + +SRC := argsmod.c +OBJS := $(SRC:.c=.o) + +obj-m += $(OBJS) + +all: + $(CHECKPATCH) -f $(SRC) || exit 1 + $(MAKE) -C $(KDIR) M=$(PWD) modules + +default: all + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean + diff --git a/03_module/argsmod.c b/03_module/argsmod.c new file mode 100644 index 0000000..b99a26f --- /dev/null +++ b/03_module/argsmod.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Victor Krasnoshchok "); +MODULE_DESCRIPTION("Compute sum & diff of mod's args upon init/exit."); +MODULE_VERSION("0.1"); + +static int first_arg; +static int second_arg; + +module_param(first_arg, int, 0444); /* S_IRUGO */ +MODULE_PARM_DESC(first_arg, "The the first int arg."); + +module_param(second_arg, int, 0444); +MODULE_PARM_DESC(first_arg, "The the second int arg."); + +static int __init args_test_init(void) +{ + pr_info("%d + %d = %d\n", first_arg, second_arg, (first_arg + second_arg)); + return 0; +} + +static void __exit args_test_exit(void) +{ + pr_info("%d - %d = %d\n", first_arg, second_arg, (first_arg - second_arg)); +} + +module_init(args_test_init); +module_exit(args_test_exit); + From e974d1f4659a26123bbebdd1e86c18a51815826e Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Sun, 28 Nov 2021 22:51:23 +0200 Subject: [PATCH 12/14] TASK3: Fix typos. Fix several typos and remove unnecessary commented line. Signed-off-by: Victor Krasnoshchok --- 03_module/argsmod.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/03_module/argsmod.c b/03_module/argsmod.c index b99a26f..d46cda7 100644 --- a/03_module/argsmod.c +++ b/03_module/argsmod.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -// + #include #include #include @@ -13,10 +13,10 @@ static int first_arg; static int second_arg; module_param(first_arg, int, 0444); /* S_IRUGO */ -MODULE_PARM_DESC(first_arg, "The the first int arg."); +MODULE_PARM_DESC(first_arg, "The first int arg."); module_param(second_arg, int, 0444); -MODULE_PARM_DESC(first_arg, "The the second int arg."); +MODULE_PARM_DESC(first_arg, "The second int arg."); static int __init args_test_init(void) { From c4762981f43490d7cefbf74b60ff991298c5b94f Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Sun, 28 Nov 2021 22:58:43 +0200 Subject: [PATCH 13/14] TASK3: Add module test logs. Add module compilation output dump, QEMU console logs and dmesg output. Signed-off-by: Victor Krasnoshchok --- 03_module/compilation_log.txt | 13 +++++++++++++ 03_module/console_log.txt | 32 ++++++++++++++++++++++++++++++++ 03_module/dmesg_log.txt | 12 ++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 03_module/compilation_log.txt create mode 100644 03_module/console_log.txt create mode 100644 03_module/dmesg_log.txt diff --git a/03_module/compilation_log.txt b/03_module/compilation_log.txt new file mode 100644 index 0000000..a74411b --- /dev/null +++ b/03_module/compilation_log.txt @@ -0,0 +1,13 @@ +victor@orion5491  ~/work/gl_kernel_procamp_2021/03_module   task03  make +~/work/buildroot-2021.02.7/output/build/linux-5.10.7/scripts/checkpatch.pl -f argsmod.c || exit 1 +total: 0 errors, 0 warnings, 34 lines checked + +argsmod.c has no obvious style problems and is ready for submission. +make -C ~/work/buildroot-2021.02.7/output/build/linux-5.10.7 M=/home/victor/work/gl_kernel_procamp_2021/03_module modules +make[1]: Entering directory '/home/victor/work/buildroot-2021.02.7/output/build/linux-5.10.7' + CC [M] /home/victor/work/gl_kernel_procamp_2021/03_module/argsmod.o + MODPOST /home/victor/work/gl_kernel_procamp_2021/03_module/Module.symvers +WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. + CC [M] /home/victor/work/gl_kernel_procamp_2021/03_module/argsmod.mod.o + LD [M] /home/victor/work/gl_kernel_procamp_2021/03_module/argsmod.ko +make[1]: Leaving directory '/home/victor/work/buildroot-2021.02.7/output/build/linux-5.10.7' diff --git a/03_module/console_log.txt b/03_module/console_log.txt new file mode 100644 index 0000000..fea22b0 --- /dev/null +++ b/03_module/console_log.txt @@ -0,0 +1,32 @@ +Starting dropbear sshd: OK + +Welcome to Buildroot +buildroot login: root +# cd /home/user/ +# ls +argsmod.ko +# insmod argsmod.ko first_arg=3 second_arg=4 +argsmod: loading out-of-tree module taints kernel. +3 + 4 = 7 +# rmmod argsmod.ko +3 - 4 = -1 +# insmod argsmod.ko first_arg=8 second_arg=6 +8 + 6 = 14 +# rmmod argsmod.ko +8 - 6 = 2 +# insmod argsmod.ko first_arg=5 +5 + 0 = 5 +# rmmod argsmod.ko +5 - 0 = 5 +# insmod argsmod.ko second_arg=10 +0 + 10 = 10 +# rmmod argsmod.ko +0 - 10 = -10 +# insmod argsmod.ko +0 + 0 = 0 +# rmmod argsmod.ko +0 - 0 = 0 +# exit + +Welcome to Buildroot +buildroot login: qemu-system-x86_64: terminating on signal 2 diff --git a/03_module/dmesg_log.txt b/03_module/dmesg_log.txt new file mode 100644 index 0000000..5ebded5 --- /dev/null +++ b/03_module/dmesg_log.txt @@ -0,0 +1,12 @@ +# dmesg +argsmod: loading out-of-tree module taints kernel. +3 + 4 = 7 +3 - 4 = -1 +8 + 6 = 14 +8 - 6 = 2 +5 + 0 = 5 +5 - 0 = 5 +0 + 10 = 10 +0 - 10 = -10 +0 + 0 = 0 +0 - 0 = 0 From e4746bb97c4d712c16e79a7d210e429cf07b0b64 Mon Sep 17 00:00:00 2001 From: Victor Krasnoshchok Date: Tue, 30 Nov 2021 19:03:48 +0200 Subject: [PATCH 14/14] TASK3: Fix second_arg param desc. Fix MODULE_PARAM_DESC for second_arg (now we call MODULE_PARAM_DESC twice for first_arg). Signed-off-by: Victor Krasnoshchok --- 03_module/argsmod.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/03_module/argsmod.c b/03_module/argsmod.c index d46cda7..6f9d69b 100644 --- a/03_module/argsmod.c +++ b/03_module/argsmod.c @@ -16,7 +16,7 @@ module_param(first_arg, int, 0444); /* S_IRUGO */ MODULE_PARM_DESC(first_arg, "The first int arg."); module_param(second_arg, int, 0444); -MODULE_PARM_DESC(first_arg, "The second int arg."); +MODULE_PARM_DESC(second_arg, "The second int arg."); static int __init args_test_init(void) {