commit a8382b30e116dbb214381dbf19ad94575ed53a14 Author: Jan Potocki Date: Mon May 4 15:56:02 2020 +0200 Initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1f14cc2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +/* + * ------------------------------------------------------------ + * "THE BEERWARE LICENSE" (Revision 42): + * wrote this code. As long as you retain this + * notice, you can do whatever you want with this stuff. If we + * meet someday, and you think this stuff is worth it, you can + * buy me a beer in return. + * ------------------------------------------------------------ +*/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..c179579 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# PWr - Sytemy operacyjne 2 P +Moje projekty z SO2 u doktora Dariusza Cabana - obsługa wątków w C++. +UWAGA: przed kompilacją trzeba zainstalować pliki nagłówkowe ncurses (Debian, Ubuntu i pochodne - pakiet libncurses5-dev) + +__Projekt 1: BRIcks Caban Kernel-thread System (_so2-bricks_)__ +Rysowanie spadających cegieł z wykorzystaniem ncurses. Założenia: +- implementacja cegły jako klasa Brick +- 1 cegła = 1 wątek +- 1 dodatkowy wątek jako monitor (rysowanie wszystkich cegieł) +- przerysowanie ekranu co 10 ms +- automatyczne dopasowywanie liczby cegieł i długość drogi do rozmiarów terminala +- wątki zrealizowane za pomocą klasy std::thread z C++11 + +__Projekt 2: SYncsys Next Caban SYStem (_so2-center_)__ +Centrum obsługi zgłoszeń. Założenia: +- co jakiś czas na ekranie pojawia się nowe zgłoszenie (jako znak) i przesuwa się do centrum obsługi +- w systemie zgłoszenie jest obsługiwane przez określony (stały) czas +- jeżeli centrum obsługi jest zajęte, kolejne zgłoszenia czekają w kolejce o ograniczonej długości +- jeżeli cała kolejka jest pełna, kolejne zgłoszenia okrążają centrum i próbują dostać się do kolejki jeszcze raz +- 1 zgłoszenie przez cały czas ma być 1 (i tym samym) wątkiem +- wykorzystanie synchronizacji, sekcji krytycznych, itp. + +__Projekt 3: BRIcks-ng Caban Kernel-thread System Next Generation (_so2-game_)__ +Prosta gra, napisana z wykorzystaniem mechanizmów poznanych na projektach 1. i 2. Zasady rozgrywki: +- czas rozgrywki -- 120 sekund +- sterowanie -- klawisze A i D +- zadanie -- łapanie na platformę spadających cegieł w kolorze takim samym, jak kolor platformy +- co 15 sekund platforma zmienia kolor +- za każdą złapaną cegłę odpowiedniego koloru przydzielane jest 5 punktów +- za każdą złapaną cegłę innego koloru odejmowany jest jeden punkt i za karę rozgrywka jest blokowana na 10 sekund - przy zliczanym wciąż czasie + +Projekty z założenia były pisane na platformy Unix-like - wszystko, gdzie są dostępne ncurses.h i unistd.h. diff --git a/so2-bricks/Brick.cpp b/so2-bricks/Brick.cpp new file mode 100644 index 0000000..da66ebe --- /dev/null +++ b/so2-bricks/Brick.cpp @@ -0,0 +1,64 @@ +#include "Brick.h" + +bool Brick::initialized = false; +int Brick::xMax; +int Brick::yMax; + +Brick::Brick(int xPosition, int descentRate) +{ + //ctor + this->xPosition = xPosition; + this->yPosition = 0; + this->descentRate = descentRate; + this->falling = false; +} + +Brick::~Brick() +{ + //dtor +} + +void Brick::initScene(int xRes, int yRes) +{ + xMax = xRes; + yMax = yRes; + initialized = true; +} + +int Brick::getxPosition() +{ + return xPosition; +} + +int Brick::getyPosition() +{ + return yPosition; +} + +bool Brick::isFalling() +{ + return falling; +} + +void Brick::fall() +{ + if(initialized) + { + falling = true; + + while(yPosition < yMax - 2) + { + yPosition++; + usleep(250000 - 10000 * descentRate); + } + } + else + { + std::cout << "Scene size not initialized!" << std::endl; + } +} + +std::thread Brick::fallThread() +{ + return std::thread(&Brick::fall, this); +} diff --git a/so2-bricks/Brick.h b/so2-bricks/Brick.h new file mode 100644 index 0000000..ddf2aa9 --- /dev/null +++ b/so2-bricks/Brick.h @@ -0,0 +1,30 @@ +#ifndef BRICK_H +#define BRICK_H + +#include +#include +#include + +class Brick +{ + public: + Brick(int xPosition, int descentRate); + ~Brick(); + static void initScene(int xRes, int yRes); + int getxPosition(); + int getyPosition(); + bool isFalling(); + std::thread fallThread(); + protected: + private: + static bool initialized; + static int xMax; + static int yMax; + int xPosition; + int yPosition; + int descentRate; + bool falling; + void fall(); +}; + +#endif // BRICK_H diff --git a/so2-bricks/Makefile b/so2-bricks/Makefile new file mode 100644 index 0000000..566540c --- /dev/null +++ b/so2-bricks/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = -Wall -std=c++11 + +OBJS = so2-bricks.o Brick.o + +LIBS = -pthread -lncurses + +TARGET = so2-bricks + +$(TARGET): $(OBJS) + $(CXX) -o $(TARGET) $(OBJS) $(LIBS) + +all: $(TARGET) + +clean: + rm -f $(OBJS) $(TARGET) diff --git a/so2-bricks/so2-bricks.cpp b/so2-bricks/so2-bricks.cpp new file mode 100644 index 0000000..e422d34 --- /dev/null +++ b/so2-bricks/so2-bricks.cpp @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "Brick.h" + +bool running = true, climate = false; +std::vector bricks; + + +// "Monitor" function +void refreshScreen() +{ + while(running == true) + { + clear(); + + for(int i = 0; i < bricks.size(); i++) + { + // Easter egg (1) + if(climate) + { + mvprintw(bricks[i].getyPosition(), bricks[i].getxPosition(), "*" ); + } + else + { + mvprintw(bricks[i].getyPosition(), bricks[i].getxPosition(), "#" ); + } + } + + refresh(); + + // Refresh every 0.01 s + usleep(10000); + } +} + +int main(int argc, char *argv[]) +{ + int xMax, yMax, fallingBricks = 0; + std::vector brickThreads; + + srand(time(0)); + + // Easter egg (2) + if(argc == 2) + { + std::string param(argv[1]); + + if(param == "--globalwarming") + { + climate = true; + } + } + + // Initialize ncurses + initscr(); + curs_set(0); + getmaxyx(stdscr, yMax, xMax); + + // Initialize scene + Brick::initScene(xMax, yMax); + + // Initialize all bricks... + for(int i = 0; i < xMax; i++) + { + // ...with random descent rate in range 0 (slow) to 20 (fast) + Brick brick(i, rand() % 21); + bricks.push_back(brick); + } + + // Start monitor + std::thread monitor(refreshScreen); + + while(fallingBricks < xMax) + { + // Determine random brick... + int randBrick = rand() % xMax; + while(bricks.at(randBrick).isFalling()) + { + // ...which still isn't falling down... + randBrick = rand() % xMax; + } + + // ...and launch it with nuclear-powered hammer ;-) + brickThreads.push_back(bricks.at(randBrick).fallThread()); + fallingBricks++; + + // Random time in range 100 to 500 ms until next fall + unsigned randTime = rand() % 5 + 1; + usleep(100000 * randTime); + } + + // Wait for all bricks + for(int i = 0; i < brickThreads.size(); i++) + { + brickThreads.at(i).join(); + } + + // Stop monitor + sleep(1); + running = false; + monitor.join(); + + // Close ncurses + endwin(); + + std::cout << "BRIcks Caban Kernel-thread System v1.1" << std::endl; + std::cout << "Jan Potocki 2018" << std::endl; + std::cout << "(beerware)" << std::endl; + std::cout << std::endl; + std::cout << '"' << "I had a Type-4 keyboard," << std::endl; + std::cout << "Bought with my Sun workstation," << std::endl; + std::cout << "Hacked on it 'til my fingers bled." << std::endl; + std::cout << "Was the winter of '95..." << '"' << std::endl; + + // Easter egg (3) + if(climate) + { + std::cout << std::endl; + std::cout << "SEVERE WEATHER ALERT: major snowfall predicted in 48h forecast for Lower Silesia, south-western Poland" << std::endl; + std::cout << "Global warming affecting again!... ;-)" << std::endl; + } + else + { + std::cout << "Beware of BRICKS! ;-)" << std::endl; + } + + return 0; +} diff --git a/so2-center/Entry.cpp b/so2-center/Entry.cpp new file mode 100644 index 0000000..14aaa8b --- /dev/null +++ b/so2-center/Entry.cpp @@ -0,0 +1,158 @@ +#include +#include +#include "Entry.h" + +bool Entry::initialized = false; +int Entry::xMax; +int Entry::yMax; + +bool full; +std::mutex state; +std::mutex ready1; +std::mutex ready2; +std::mutex service; + +Entry::Entry(int speedRate) +{ + //ctor + this->xPosition = -1; + this->yPosition = yMax / 2; + this->speedRate = speedRate; + this->symbol = "*"; + + if(initialized == false) + { + std::cout << "WARNING: Scene size not initialized!" << std::endl; + } +} + +Entry::~Entry() +{ + //dtor +} + +void Entry::initScene(int xRes, int yRes) +{ + xMax = xRes; + yMax = yRes; + initialized = true; +} + +int Entry::getxPosition() +{ + return xPosition; +} + +int Entry::getyPosition() +{ + return yPosition; +} + +std::string Entry::getSymbol() +{ + return symbol; +} + +void Entry::run() +{ + if(initialized) + { + while(xPosition < xMax / 2) + { + xPosition++; + usleep(100000 - 10000 * speedRate); + } + + bool goAround; + + state.lock(); + if(full == true) + { + goAround = true; + } + else + { + goAround = false; + full = true; + ready2.lock(); + } + state.unlock(); + + while(goAround) + { + while(yPosition < 3 * yMax / 4) + { + yPosition++; + usleep(100000 - 10000 * speedRate); + } + + while(xPosition < 3 * xMax / 4) + { + xPosition++; + usleep(100000 - 10000 * speedRate); + } + + while(yPosition > yMax / 4) + { + yPosition--; + usleep(100000 - 10000 * speedRate); + } + + while(xPosition > xMax / 2) + { + xPosition--; + usleep(100000 - 10000 * speedRate); + } + + while(yPosition < yMax / 2) + { + yPosition++; + usleep(100000 - 10000 * speedRate); + } + + state.lock(); + if(full == false) + { + goAround = false; + full = true; + ready2.lock(); + } + state.unlock(); + } + + xPosition++; + symbol = "2"; + + ready1.lock(); + state.lock(); + full = false; + state.unlock(); + ready2.unlock(); + xPosition++; + symbol = "1"; + + service.lock(); + ready1.unlock(); + xPosition++; + symbol = "#"; + sleep(5); + + service.unlock(); + symbol = "*"; + + while(xPosition < xMax) + { + xPosition++; + usleep(100000 - 10000 * speedRate); + } + } + else + { + std::cout << "ERROR: Scene size not initialized!" << std::endl; + } +} + +std::thread Entry::runThread() +{ + return std::thread(&Entry::run, this); +} diff --git a/so2-center/Entry.h b/so2-center/Entry.h new file mode 100644 index 0000000..6edc5f1 --- /dev/null +++ b/so2-center/Entry.h @@ -0,0 +1,30 @@ +#ifndef ENTRY_H +#define ENTRY_H + +#include +#include +#include + +class Entry +{ + public: + Entry(int speedRate); + ~Entry(); + static void initScene(int xRes, int yRes); + int getxPosition(); + int getyPosition(); + std::string getSymbol(); + std::thread runThread(); + protected: + private: + static bool initialized; + static int xMax; + static int yMax; + int xPosition; + int yPosition; + int speedRate; + std::string symbol; + void run(); +}; + +#endif // ENTRY_H diff --git a/so2-center/Entry.o b/so2-center/Entry.o new file mode 100644 index 0000000..55c72f1 Binary files /dev/null and b/so2-center/Entry.o differ diff --git a/so2-center/Makefile b/so2-center/Makefile new file mode 100644 index 0000000..c1e9123 --- /dev/null +++ b/so2-center/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = -Wall -std=c++11 + +OBJS = so2-center.o Entry.o + +LIBS = -pthread -lncurses + +TARGET = so2-center + +$(TARGET): $(OBJS) + $(CXX) -o $(TARGET) $(OBJS) $(LIBS) + +all: $(TARGET) + +clean: + rm -f $(OBJS) $(TARGET) diff --git a/so2-center/so2-center.cpp b/so2-center/so2-center.cpp new file mode 100644 index 0000000..5c3168f --- /dev/null +++ b/so2-center/so2-center.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Entry.h" + +bool running = true; +std::vector entries; + +std::mutex display; + +// "Monitor" function +void refreshScreen() +{ + while(running == true) + { + clear(); + + for(int i = 0; i < entries.size(); i++) + { + mvprintw(entries[i].getyPosition(), entries[i].getxPosition(), entries[i].getSymbol().c_str()); + } + + refresh(); + + // Refresh every 0.01 s + usleep(10000); + } +} + +int main() +{ + int xMax, yMax, fallingBricks = 0; + std::vector entriesThreads; + + srand(time(0)); + + // Initialize ncurses + initscr(); + curs_set(0); + getmaxyx(stdscr, yMax, xMax); + + // Initialize scene + Entry::initScene(xMax, yMax); + + // Start monitor + std::thread monitor(refreshScreen); + + + for(int i = 0; i < 15; i++) + { + entries.push_back(*(new Entry(rand() % 6))); + } + + for(int i = 0; i < entries.size(); i++) + { + unsigned randTime = rand() % 3 + 1; + sleep(1 * randTime); + entriesThreads.push_back(entries[i].runThread()); + } + + // Wait for all entries + for(int i = 0; i < entriesThreads.size(); i++) + { + entriesThreads.at(i).join(); + } + + // Stop monitor + sleep(1); + running = false; + monitor.join(); + + // Close ncurses + endwin(); + + std::cout << "SYncsys Next Caban SYStem v1.1" << std::endl; + std::cout << "Jan Potocki 2018" << std::endl; + std::cout << "(beerware)" << std::endl; + std::cout << std::endl; + std::cout << '"' << "...And now the times have changed" << std::endl; + std::cout << "Repos on the web, git," << std::endl; + std::cout << "Now githubs everywhere." << std::endl; + std::cout << "Not like the winter of '95..." << '"' << std::endl; + + return 0; +} diff --git a/so2-center/so2-center.o b/so2-center/so2-center.o new file mode 100644 index 0000000..865500c Binary files /dev/null and b/so2-center/so2-center.o differ diff --git a/so2-game/Brick.cpp b/so2-game/Brick.cpp new file mode 100644 index 0000000..19ac8f0 --- /dev/null +++ b/so2-game/Brick.cpp @@ -0,0 +1,126 @@ +#include "Brick.h" +#include +#include +#include + +Platform *Brick::platform = 0; + +Brick::Brick(int xPosition, int descentRate) +{ + //ctor + this->xPosition = xPosition; + this->yPosition = -1; + this->descentRate = descentRate; + this->falling = false; + + randomColor(); +} + +Brick::~Brick() +{ + //dtor +} + +void Brick::setPlatform(Platform *newPlatform) +{ + platform = newPlatform; +} + +int Brick::getxPosition() +{ + return xPosition; +} + +int Brick::getyPosition() +{ + return yPosition; +} + +int Brick::getColor() +{ + return color; +} + + +bool Brick::isFalling() +{ + return falling; +} + +void Brick::fall() +{ + if(!initialized) + { + std::cout << "Scene size not initialized!" << std::endl; + return; + } + + if(platform == 0) + { + std::cout << "Platform not set!" << std::endl; + return; + } + + falling = true; + + while(running & falling & yPosition < yMax - 2) + { + // Freeze game + if(freezed) + { + std::unique_lock freezeLock(freezeMutex); + + while(freezed) + { + freezeCondition.wait(freezeLock); + } + } + + // If game terminated, we shouldn't do all this stuff + if(running) + { + yPosition++; + + if(yPosition == yMax - 2 && platform->getPosition() <= xPosition && platform->getEnd() >= xPosition) + { + falling = false; + + if(platform->getColor() == color) + { + points += 5; + } + else + { + if(points != 0) + { + points--; + } + + freeze(); + } + + break; + } + + usleep(250000 - 10000 * descentRate); + } + } + + if(running) + { + // Reset + yPosition = -1; + falling = false; + randomColor(); + } +} + +std::thread Brick::fallThread() +{ + return std::thread(&Brick::fall, this); +} + +void Brick::randomColor() +{ + color = rand() % 6 + 1; +} diff --git a/so2-game/Brick.h b/so2-game/Brick.h new file mode 100644 index 0000000..4c2e0f4 --- /dev/null +++ b/so2-game/Brick.h @@ -0,0 +1,31 @@ +#ifndef BRICK_H +#define BRICK_H + +#include +#include "Platform.h" +#include "Scene.h" + +class Brick : public Scene +{ + public: + Brick(int xPosition, int descentRate); + ~Brick(); + static void setPlatform(Platform *newPlatform); + int getxPosition(); + int getyPosition(); + int getColor(); + bool isFalling(); + std::thread fallThread(); + protected: + private: + static Platform *platform; + int xPosition; + int yPosition; + int descentRate; + int color; + bool falling; + void fall(); + void randomColor(); +}; + +#endif // BRICK_H diff --git a/so2-game/Brick.o b/so2-game/Brick.o new file mode 100644 index 0000000..a16b1f2 Binary files /dev/null and b/so2-game/Brick.o differ diff --git a/so2-game/Makefile b/so2-game/Makefile new file mode 100644 index 0000000..ee46a2d --- /dev/null +++ b/so2-game/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = -Wall -std=c++11 + +OBJS = so2-game.o Brick.o Platform.o Scene.o Stopwatch.o + +LIBS = -pthread -lncurses + +TARGET = so2-game + +$(TARGET): $(OBJS) + $(CXX) -o $(TARGET) $(OBJS) $(LIBS) + +all: $(TARGET) + +clean: + rm -f $(OBJS) $(TARGET) diff --git a/so2-game/Platform.cpp b/so2-game/Platform.cpp new file mode 100644 index 0000000..769dabd --- /dev/null +++ b/so2-game/Platform.cpp @@ -0,0 +1,133 @@ +#include "Platform.h" +#include +#include +#include + +Platform::Platform() +{ + //ctor + points = 0; + color = 0; + sprite = "<--->"; +} + +Platform::~Platform() +{ + //dtor +} + +const char *Platform::getSprite() +{ + return sprite.c_str(); +} + +int Platform::getPosition() +{ + return position; +} + +int Platform::getEnd() +{ + int endPos = position + sprite.length() - 1; + return endPos; +} + +int Platform::getColor() +{ + return color; +} + +void Platform::terminateThreads() +{ + running = false; +} + +void Platform::moveKey() +{ + if(!initialized) + { + std::cout << "Scene size not initialized!" << std::endl; + return; + } + + int key; + + while(running) + { + ncursesMutex.lock(); + key = getch(); + ncursesMutex.unlock(); + + // Freeze game + if(freezed) + { + std::unique_lock freezeLock(freezeMutex); + + while(freezed) + { + freezeCondition.wait(freezeLock); + } + + // Ignore keys pressed when frozen + flushinp(); + } + + switch(key) + { + case 'a': + if(position > 0) + { + position--; + } + break; + case 'd': + if(position < xMax - sprite.length()) + { + position++; + } + } + } +} + +std::thread Platform::moveKeyThread() +{ + return std::thread(&Platform::moveKey, this); +} + +void Platform::colorChange() +{ + const int idleSeconds = 15; + + while(running) + { + color = rand() % 6 + 1; + + // Waiting - this must be interruptable, so it can't be just sleep(idleSeconds) + for(int i = 0; i < idleSeconds * 10; i++) + { + // Freeze game + if(freezed) + { + std::unique_lock freezeLock(freezeMutex); + + while(freezed) + { + freezeCondition.wait(freezeLock); + } + } + + if(!running) + { + break; + } + + // Every tick = 100 ms + usleep(100000); + } + } +} + +std::thread Platform::colorChangeThread() +{ + return std::thread(&Platform::colorChange, this); +} diff --git a/so2-game/Platform.h b/so2-game/Platform.h new file mode 100644 index 0000000..50dad14 --- /dev/null +++ b/so2-game/Platform.h @@ -0,0 +1,30 @@ +#ifndef PLATFORM_H +#define PLATFORM_H + +#include +#include +#include "Scene.h" + +class Platform : public Scene +{ + public: + Platform(); + ~Platform(); + const char *getSprite(); + int getPosition(); + int getEnd(); + int getColor(); + void terminateThreads(); + std::thread moveKeyThread(); + std::thread colorChangeThread(); + protected: + private: + std::string sprite; + int position; + int points; + int color; + void moveKey(); + void colorChange(); +}; + +#endif // PLATFORM_H diff --git a/so2-game/Platform.o b/so2-game/Platform.o new file mode 100644 index 0000000..135dc56 Binary files /dev/null and b/so2-game/Platform.o differ diff --git a/so2-game/Scene.cpp b/so2-game/Scene.cpp new file mode 100644 index 0000000..6b626fa --- /dev/null +++ b/so2-game/Scene.cpp @@ -0,0 +1,71 @@ +#include "Scene.h" +#include + +int Scene::xMax; +int Scene::yMax; +int Scene::points = 0; +bool Scene::initialized = false; +bool Scene::freezed = false; +bool Scene::running = false; +std::mutex Scene::ncursesMutex; +std::mutex Scene::freezeMutex; +std::condition_variable Scene::freezeCondition; + +Scene::Scene() +{ + //ctor +} + +Scene::~Scene() +{ + //dtor +} + +void Scene::init(int xRes, int yRes) +{ + xMax = xRes; + yMax = yRes; + running = true; + initialized = true; +} + +int Scene::getPoints() +{ + return points; +} + +bool Scene::isFreezed() +{ + return freezed; +} + +void Scene::freeze() +{ + const int idleSeconds = 10; + + std::unique_lock freezeLock(freezeMutex); + freezed = true; + sleep(idleSeconds); + +// // This must be interruptable, so it can't be just sleep(idleSeconds) +// for(int i = 0; i < idleSeconds * 10; i++) +// { +// if(running) +// { +// // Every tick = 100 ms +// usleep(100000); +// } +// else +// { +// break; +// } +// } + + freezed = false; + freezeCondition.notify_all(); +} + +void Scene::terminateAll() +{ + Scene::running = false; +} diff --git a/so2-game/Scene.h b/so2-game/Scene.h new file mode 100644 index 0000000..91d4d88 --- /dev/null +++ b/so2-game/Scene.h @@ -0,0 +1,31 @@ +#ifndef SCENE_H +#define SCENE_H + +#include +#include +#include + +class Scene +{ + public: + static std::mutex ncursesMutex; + static std::mutex freezeMutex; + static std::condition_variable freezeCondition; + Scene(); + virtual ~Scene(); + static void init(int xRes, int yRes); + static int getPoints(); + static bool isFreezed(); + static void terminateAll(); + protected: + static int xMax; + static int yMax; + static int points; + static bool initialized; + static bool freezed; + static bool running; + static void freeze(); + private: +}; + +#endif // SCENE_H diff --git a/so2-game/Scene.o b/so2-game/Scene.o new file mode 100644 index 0000000..537bf2a Binary files /dev/null and b/so2-game/Scene.o differ diff --git a/so2-game/Stopwatch.cpp b/so2-game/Stopwatch.cpp new file mode 100644 index 0000000..03f0d39 --- /dev/null +++ b/so2-game/Stopwatch.cpp @@ -0,0 +1,40 @@ +#include "Stopwatch.h" + +Stopwatch::Stopwatch() +{ + //ctor + // Jan Potocki 2018 +} + +void Stopwatch::start() +{ + running = true; + miliseconds = 0; + measureThread = std::thread(&Stopwatch::measure, this); +} + +void Stopwatch::stop() +{ + running = false; + measureThread.join(); +} + +bool Stopwatch::isRunning() +{ + return running; +} + +float Stopwatch::read() +{ + float measurement = (float)miliseconds / 1000; + return measurement; +} + +void Stopwatch::measure() +{ + while(running) + { + usleep(1000); + miliseconds++; + } +} diff --git a/so2-game/Stopwatch.h b/so2-game/Stopwatch.h new file mode 100644 index 0000000..2963881 --- /dev/null +++ b/so2-game/Stopwatch.h @@ -0,0 +1,25 @@ +#ifndef STOPWATCH_H +#define STOPWATCH_H + +#include +#include + +// Klasa do pomiaru czasu w oparciu o funkcje systemowe +// Jan Potocki 2018 +class Stopwatch +{ + public: + Stopwatch(); + void start(); + void stop(); + bool isRunning(); + float read(); + protected: + private: + unsigned long long int miliseconds; + bool running; + std::thread measureThread; + void measure(); +}; + +#endif // STOPWATCH_H diff --git a/so2-game/Stopwatch.o b/so2-game/Stopwatch.o new file mode 100644 index 0000000..c64ab6a Binary files /dev/null and b/so2-game/Stopwatch.o differ diff --git a/so2-game/so2-game.cpp b/so2-game/so2-game.cpp new file mode 100644 index 0000000..1c4148f --- /dev/null +++ b/so2-game/so2-game.cpp @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "Brick.h" +#include "Platform.h" +#include "Stopwatch.h" + +const int gameTime = 120; + +int xMax, yMax; +bool refreshing = false; +bool climate = false; +std::vector bricks; +Platform platform; +Stopwatch gameClock; + +// "Monitor" function +void refreshScreen() +{ + while(refreshing == true) + { + Scene::ncursesMutex.lock(); + clear(); + + for(int i = 0; i < bricks.size(); i++) + { + // Easter egg (1) + if(climate) + { + attron(COLOR_PAIR(bricks[i].getColor())); + mvprintw(bricks[i].getyPosition(), bricks[i].getxPosition(), "*"); + attroff(COLOR_PAIR(bricks[i].getColor())); + } + else + { + attron(COLOR_PAIR(bricks[i].getColor())); + mvprintw(bricks[i].getyPosition(), bricks[i].getxPosition(), "#"); + attroff(COLOR_PAIR(bricks[i].getColor())); + } + } + + attron(COLOR_PAIR(platform.getColor())); + mvprintw(yMax - 2, platform.getPosition(), platform.getSprite()); + attroff(COLOR_PAIR(platform.getColor())); + + mvprintw(yMax - 1, 0, "%.3f", gameClock.read()); + mvprintw(yMax - 1, xMax - 3, "%.3d", Scene::getPoints()); + + refresh(); + Scene::ncursesMutex.unlock(); + + // Refresh every 0.01 s + usleep(10000); + } +} + +int main(int argc, char *argv[]) +{ + std::vector brickThreads; + + srand(time(0)); + + // Easter egg (2) + if(argc == 2) + { + std::string param(argv[1]); + + if(param == "--globalwarming") + { + climate = true; + } + } + + // Initialize ncurses + initscr(); + curs_set(0); + getmaxyx(stdscr, yMax, xMax); + + // Initialize colors + start_color(); + init_pair(1, COLOR_RED, COLOR_BLACK); + init_pair(2, COLOR_GREEN, COLOR_BLACK); + init_pair(3, COLOR_YELLOW, COLOR_BLACK); + init_pair(4, COLOR_BLUE, COLOR_BLACK); + init_pair(5, COLOR_MAGENTA, COLOR_BLACK); + init_pair(6, COLOR_CYAN, COLOR_BLACK); + + // Non-blocking input for platform-movement + timeout(0); + + // Initialize scene + Scene::init(xMax, yMax); + Brick::setPlatform(&platform); + + // Initialize all bricks... + for(int i = 0; i < xMax; i++) + { + // ...with random descent rate in range 0 (slow) to 15 (fast) + Brick brick(i, rand() % 16); + bricks.push_back(brick); + } + + // Start monitor + refreshing = true; + std::thread monitor(refreshScreen); + + // Start platform treads + std::thread platformMover(platform.moveKeyThread()); + std::thread platformColorChanger(platform.colorChangeThread()); + + // Start game + gameClock.start(); + + while(gameClock.read() < gameTime) + { + // Freeze game + if(Scene::isFreezed() == true) + { + std::unique_lock freezeLock(Scene::freezeMutex); + + while(Scene::isFreezed() == true) + { + Scene::freezeCondition.wait(freezeLock); + } + } + + // Determine random brick... + int randBrick = rand() % xMax; + while(bricks.at(randBrick).isFalling()) + { + // ...which still isn't falling down... + randBrick = rand() % xMax; + } + + // ...and launch it with nuclear-powered hammer ;-) + brickThreads.push_back(bricks.at(randBrick).fallThread()); + + // Random time in range 400 to 800 ms until next fall + unsigned randTime = rand() % 4 + 4; + usleep(100000 * randTime); + } + + // Stop clock + gameClock.stop(); + + // Stop scene objects threads + Scene::terminateAll(); + + platformMover.join(); + platformColorChanger.join(); + + for(int i = 0; i < brickThreads.size(); i++) + { + brickThreads.at(i).join(); + } + + // Stop monitor + refreshing = false; + monitor.join(); + sleep(1); + + // Close ncurses + endwin(); + + std::cout << "Your score: " << Scene::getPoints() << " points" << std::endl; + + if(Scene::getPoints() != 0) + { + std::cout << "Congratulations!" << std::endl; + } + + std::cout << std::endl; + + std::cout << "BRIcks-ng Caban Kernel-thread System Next Generation v1.0" << std::endl; + std::cout << "Jan Potocki 2018" << std::endl; + std::cout << "(beerware)" << std::endl; + std::cout << std::endl; + std::cout << '"' << "...Back around that Halloween," << std::endl; + std::cout << "Microsoft said open source would never last," << std::endl; + std::cout << "But now they use the repo tools," << std::endl; + std::cout << "In the same open access way..." << '"' << std::endl; + std::cout << "(and recently acquired GitHub)" << std::endl; + std::cout << std::endl; + + // Easter egg (3) + if(climate) + { + std::cout << "SEVERE WEATHER ALERT: major snowfall predicted in 48h forecast for Lower Silesia, south-western Poland" << std::endl; + std::cout << "Global warming affecting again!... ;-)" << std::endl; + } + else + { + std::cout << "Beware of BRICKS! ;-)" << std::endl; + } + + return 0; +} diff --git a/so2-game/so2-game.o b/so2-game/so2-game.o new file mode 100644 index 0000000..e922535 Binary files /dev/null and b/so2-game/so2-game.o differ