400 lines
12 KiB
Diff
400 lines
12 KiB
Diff
From 34fe75c0a83ac9cc674f61de8a96db7bf6133b32 Mon Sep 17 00:00:00 2001
|
||
From: Angela <bwembael96241@th-nuernberg.de>
|
||
Date: Mon, 1 Dec 2025 15:24:18 +0100
|
||
Subject: [PATCH 6/6] Fix Memory Game Race Conditions with GameState + add
|
||
threading for measurements
|
||
|
||
---
|
||
CMakeLists.txt | 47 +++++++++++--
|
||
bindings.cpp | 19 ++++++
|
||
gamecube.cpp | 5 ++
|
||
main.cpp | 172 +++++++++++++++++++++++++++++++++++------------
|
||
measurements.txt | 0
|
||
5 files changed, 195 insertions(+), 48 deletions(-)
|
||
create mode 100644 bindings.cpp
|
||
create mode 100644 measurements.txt
|
||
|
||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||
index ef310f2..126e7e8 100644
|
||
--- a/CMakeLists.txt
|
||
+++ b/CMakeLists.txt
|
||
@@ -1,23 +1,60 @@
|
||
cmake_minimum_required(VERSION 3.15)
|
||
-project(Programmieren_3b)
|
||
+project(Programmieren_3b LANGUAGES CXX)
|
||
|
||
set(CMAKE_CXX_STANDARD 17)
|
||
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||
|
||
include(FetchContent)
|
||
|
||
+# -----------------------------
|
||
+# Raylib
|
||
+# -----------------------------
|
||
FetchContent_Declare(
|
||
raylib
|
||
GIT_REPOSITORY https://github.com/raysan5/raylib.git
|
||
GIT_TAG 5.0
|
||
)
|
||
-
|
||
FetchContent_MakeAvailable(raylib)
|
||
|
||
-add_executable(Prog3B
|
||
+# -----------------------------
|
||
+# pybind11
|
||
+# -----------------------------
|
||
+FetchContent_Declare(
|
||
+ pybind11
|
||
+ GIT_REPOSITORY https://github.com/pybind/pybind11.git
|
||
+ GIT_TAG v2.11.1
|
||
+)
|
||
+FetchContent_MakeAvailable(pybind11)
|
||
+
|
||
+# -----------------------------
|
||
+# Executable für das Spiel
|
||
+# -----------------------------
|
||
+add_executable(Programmieren_3b
|
||
main.cpp
|
||
gamecube.cpp
|
||
gamematrix.cpp
|
||
)
|
||
|
||
-target_include_directories(Prog3B PRIVATE .)
|
||
-target_link_libraries(Prog3B raylib)
|
||
+target_link_libraries(Programmieren_3b PRIVATE raylib)
|
||
+
|
||
+# Windows-spezifische Abhängigkeiten für raylib
|
||
+if(WIN32)
|
||
+ target_link_libraries(Programmieren_3b PRIVATE opengl32 gdi32 winmm kernel32)
|
||
+endif()
|
||
+
|
||
+target_include_directories(Programmieren_3b PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||
+
|
||
+# -----------------------------
|
||
+# Python-Modul
|
||
+# -----------------------------
|
||
+pybind11_add_module(gamematrix_python
|
||
+ bindings.cpp
|
||
+ gamematrix.cpp
|
||
+)
|
||
+
|
||
+# Python-Modul braucht die Header
|
||
+target_include_directories(gamematrix_python PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||
+
|
||
+# Raylib ist für das Python-Modul nicht nötig, nur für C++ Executable
|
||
+# Optional: Falls du Gamematrix aus Python auch mit Raylib testen willst, kann man linken
|
||
+# target_link_libraries(gamematrix_python PRIVATE raylib)
|
||
diff --git a/bindings.cpp b/bindings.cpp
|
||
new file mode 100644
|
||
index 0000000..32e029d
|
||
--- /dev/null
|
||
+++ b/bindings.cpp
|
||
@@ -0,0 +1,19 @@
|
||
+#include <pybind11/pybind11.h>
|
||
+#include <pybind11/stl.h>
|
||
+#include "gamematrix.h"
|
||
+#include "raylib.h"
|
||
+#include "rlgl.h"
|
||
+
|
||
+namespace py = pybind11;
|
||
+
|
||
+PYBIND11_MODULE(gamematrix, m) {
|
||
+ m.doc() = "gamematrix library exposed to Python";
|
||
+
|
||
+ m.def("matmul", &gameMatrix::matmul);
|
||
+
|
||
+ m.def("translate", [](double x, double y, double z) {
|
||
+ return gameMatrix::translate({x, y, z});
|
||
+ });
|
||
+
|
||
+ m.def("rot3D", &gameMatrix::rot3D);
|
||
+}
|
||
diff --git a/gamecube.cpp b/gamecube.cpp
|
||
index 390dbf7..692691d 100644
|
||
--- a/gamecube.cpp
|
||
+++ b/gamecube.cpp
|
||
@@ -60,3 +60,8 @@ void gamecube::Draw() const
|
||
|
||
Vec3 gamecube::GetPosition() const { return position; }
|
||
float gamecube::GetRotationY() const { return rotation; }
|
||
+bool gamecube::IsFullyFlipped() const
|
||
+{
|
||
+ return rotation == 180.0f;
|
||
+}
|
||
+
|
||
diff --git a/main.cpp b/main.cpp
|
||
index 035accf..e82e793 100644
|
||
--- a/main.cpp
|
||
+++ b/main.cpp
|
||
@@ -1,18 +1,30 @@
|
||
#include "gamecube.h"
|
||
#include <algorithm>
|
||
+#include <iostream>
|
||
+#include <fstream>
|
||
#include <ctime>
|
||
#include <vector>
|
||
+#include <thread>
|
||
+#include <chrono>
|
||
|
||
// -----------------------------------------------------------
|
||
-// 3D Memory Game – Hauptprogramm
|
||
+// 3D Memory Game – Hauptprogramm mit GameState
|
||
// -----------------------------------------------------------
|
||
+
|
||
+// Zustände laut Aufgabenblatt
|
||
+enum class GameState
|
||
+{
|
||
+ Idle, // kein Würfel offen
|
||
+ OneFlipped, // ein Würfel offen
|
||
+ LockInput, // Würfel drehen -> Eingabe blockiert
|
||
+ CheckingMatch // beide offen, Vergleich läuft
|
||
+};
|
||
+
|
||
int main()
|
||
{
|
||
- // Zufall initialisieren
|
||
srand(time(NULL));
|
||
|
||
- // Fenster und Kamera
|
||
- InitWindow(800, 600, "3D Memory Game with Matrix3D Library");
|
||
+ InitWindow(800, 600, "3D Memory Game with GameState");
|
||
SetTargetFPS(60);
|
||
|
||
Camera3D camera{};
|
||
@@ -22,86 +34,104 @@ int main()
|
||
camera.fovy = 45.0f;
|
||
camera.projection = CAMERA_PERSPECTIVE;
|
||
|
||
- // Farben für 3 Paare
|
||
Color colors[] = { RED, GREEN, BLUE };
|
||
|
||
- // 6 Karten-Positionen im 3x2 Raster
|
||
std::vector<Vec3> positions = {
|
||
{-2, 0, -2}, {0, 0, -2}, {2, 0, -2},
|
||
{-2, 0, 0}, {0, 0, 0}, {2, 0, 0}
|
||
};
|
||
|
||
- // Doppelte Farben in einen Pool und mischen
|
||
- std::vector<Color> colorPool;
|
||
- for (int i = 0; i < 3; i++)
|
||
- {
|
||
- colorPool.push_back(colors[i]);
|
||
- colorPool.push_back(colors[i]);
|
||
+ // Farben doppelt
|
||
+ std::vector<Color> pool;
|
||
+ for (int i = 0; i < 3; i++) {
|
||
+ pool.push_back(colors[i]);
|
||
+ pool.push_back(colors[i]);
|
||
}
|
||
|
||
- for (int i = colorPool.size() - 1; i > 0; --i)
|
||
- {
|
||
- int j = rand() % (i + 1);
|
||
- std::swap(colorPool[i], colorPool[j]);
|
||
- }
|
||
+ // mischen
|
||
+ for (int i = pool.size() - 1; i > 0; --i)
|
||
+ std::swap(pool[i], pool[rand() % (i + 1)]);
|
||
|
||
- // Karten/Würfel erstellen
|
||
+ // Würfel erzeugen
|
||
std::vector<gamecube> cubes;
|
||
for (int i = 0; i < 6; i++)
|
||
- cubes.emplace_back(positions[i], colorPool[i]);
|
||
+ cubes.emplace_back(positions[i], pool[i]);
|
||
|
||
+ // GameState Variablen
|
||
+ GameState state = GameState::Idle;
|
||
gamecube* first = nullptr;
|
||
gamecube* second = nullptr;
|
||
- float flipSpeed = 5.0f;
|
||
bool gameWon = false;
|
||
+ float flipSpeed = 5.0f;
|
||
|
||
// -----------------------------------------------------------
|
||
- // Hauptspielschleife
|
||
+ // Game Loop
|
||
// -----------------------------------------------------------
|
||
while (!WindowShouldClose())
|
||
{
|
||
- // Klick-Erkennung
|
||
- if (!gameWon && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
|
||
+ Vector2 mouse = GetMousePosition();
|
||
+
|
||
+ // -------------------------------------------------------
|
||
+ // Eingabe NUR erlauben, wenn Idle oder OneFlipped
|
||
+ // -------------------------------------------------------
|
||
+ if (!gameWon &&
|
||
+ (state == GameState::Idle || state == GameState::OneFlipped) &&
|
||
+ IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
|
||
{
|
||
- Vector2 mouse = GetMousePosition();
|
||
-
|
||
for (auto &c : cubes)
|
||
{
|
||
if (!c.IsFlipped() && !c.IsMatched())
|
||
{
|
||
- Vector2 screenPos = GetWorldToScreen(
|
||
+ Vector2 screen = GetWorldToScreen(
|
||
{c.GetPosition().x, c.GetPosition().y, c.GetPosition().z},
|
||
camera
|
||
);
|
||
|
||
- if (fabs(mouse.x - screenPos.x) < 40 &&
|
||
- fabs(mouse.y - screenPos.y) < 40)
|
||
+ if (fabs(mouse.x - screen.x) < 40 &&
|
||
+ fabs(mouse.y - screen.y) < 40)
|
||
{
|
||
- c.FlipForward();
|
||
+ c.FlipForward(); // Animation starten
|
||
+
|
||
+ if (state == GameState::Idle)
|
||
+ {
|
||
+ first = &c;
|
||
+ state = GameState::OneFlipped;
|
||
+ }
|
||
+ else if (state == GameState::OneFlipped)
|
||
+ {
|
||
+ second = &c;
|
||
+ state = GameState::LockInput; // Animation läuft
|
||
+ }
|
||
+ break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
- // Animation
|
||
+ // -------------------------------------------------------
|
||
+ // Animation durchführen
|
||
+ // -------------------------------------------------------
|
||
for (auto &c : cubes)
|
||
- {
|
||
c.Update(flipSpeed);
|
||
|
||
- if (c.IsFlipped() && !c.IsMatched())
|
||
- {
|
||
- if (!first) first = &c;
|
||
- else if (!second && &c != first) second = &c;
|
||
- }
|
||
+ // Wenn beide cubes fertig gedreht → Vergleich starten
|
||
+ if (state == GameState::LockInput &&
|
||
+ first && second &&
|
||
+ first->IsFullyFlipped() &&
|
||
+ second->IsFullyFlipped())
|
||
+ {
|
||
+ state = GameState::CheckingMatch;
|
||
}
|
||
|
||
- // Matching
|
||
- if (first && second)
|
||
+ // -------------------------------------------------------
|
||
+ // Vergleich
|
||
+ // -------------------------------------------------------
|
||
+ if (state == GameState::CheckingMatch)
|
||
{
|
||
- Color col1 = first->GetColor();
|
||
- Color col2 = second->GetColor();
|
||
+ Color a = first->GetColor();
|
||
+ Color b = second->GetColor();
|
||
|
||
- if (col1.r == col2.r && col1.g == col2.g && col1.b == col2.b)
|
||
+ if (a.r == b.r && a.g == b.g && a.b == b.b)
|
||
{
|
||
first->SetMatched(true);
|
||
second->SetMatched(true);
|
||
@@ -113,16 +143,21 @@ int main()
|
||
}
|
||
|
||
first = second = nullptr;
|
||
+ state = GameState::Idle;
|
||
}
|
||
|
||
+ // -------------------------------------------------------
|
||
// Gewinnprüfung
|
||
+ // -------------------------------------------------------
|
||
if (!gameWon)
|
||
gameWon = std::all_of(
|
||
cubes.begin(), cubes.end(),
|
||
[](const gamecube &c){ return c.IsMatched(); }
|
||
);
|
||
|
||
- // Zeichnen
|
||
+ // -------------------------------------------------------
|
||
+ // Rendering
|
||
+ // -------------------------------------------------------
|
||
BeginDrawing();
|
||
ClearBackground(RAYWHITE);
|
||
BeginMode3D(camera);
|
||
@@ -133,12 +168,63 @@ int main()
|
||
EndMode3D();
|
||
|
||
if (gameWon)
|
||
- DrawText("Congrats! You found all pairs!", 150, 260, 30, DARKBLUE);
|
||
+ DrawText("Congrats! You found all pairs!", 140, 260, 30, DARKBLUE);
|
||
else
|
||
DrawText("Flip 2 cubes - find matching pairs!", 10, 10, 20, DARKGRAY);
|
||
|
||
EndDrawing();
|
||
}
|
||
+ std::vector<int> messungen;
|
||
+ std::ifstream file("measurements.txt");
|
||
+ int value;
|
||
+ while (file >> value) messungen.push_back(value);
|
||
+
|
||
+ // -------------------------
|
||
+ // Serielle Zählung
|
||
+ // -------------------------
|
||
+ auto start = std::chrono::steady_clock::now();
|
||
+ int increases = 0;
|
||
+ for (size_t i = 1; i < messungen.size(); ++i)
|
||
+ if (messungen[i] > messungen[i-1]) ++increases;
|
||
+ auto end = std::chrono::steady_clock::now();
|
||
+ std::cout << "Serial: " << increases
|
||
+ << " Took: "
|
||
+ << std::chrono::duration<double>(end - start).count()
|
||
+ << " s\n";
|
||
+
|
||
+ // -------------------------
|
||
+ // Parallele Zählung
|
||
+ // -------------------------
|
||
+ int numThreads = 4;
|
||
+ std::vector<int> results(numThreads, 0);
|
||
+ std::vector<std::thread> threads;
|
||
+
|
||
+ int totalComparisons = messungen.size() - 1;
|
||
+ int base = totalComparisons / numThreads;
|
||
+ int remainder = totalComparisons % numThreads;
|
||
+
|
||
+ int idx = 1;
|
||
+ for (int t = 0; t < numThreads; t++)
|
||
+ {
|
||
+ int startIdx = idx;
|
||
+ int count = base + (t < remainder ? 1 : 0);
|
||
+ int endIdx = idx + count - 1;
|
||
+ idx = endIdx + 1;
|
||
+
|
||
+ threads.emplace_back([&, t, startIdx, endIdx](){
|
||
+ int local = 0;
|
||
+ for (int i = startIdx; i <= endIdx; ++i)
|
||
+ if (messungen[i] > messungen[i-1]) ++local;
|
||
+ results[t] = local;
|
||
+ });
|
||
+ }
|
||
+
|
||
+ for (auto &th : threads) th.join();
|
||
+
|
||
+ int total = 0;
|
||
+ for (auto r : results) total += r;
|
||
+
|
||
+ std::cout << "Parallel: " << total << "\n";
|
||
|
||
CloseWindow();
|
||
return 0;
|
||
diff --git a/measurements.txt b/measurements.txt
|
||
new file mode 100644
|
||
index 0000000..e69de29
|
||
--
|
||
2.51.2.windows.1
|
||
|