Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@
**Learning:** Users often spam keys during a game's countdown phase in anticipation. If these inputs are buffered and processed immediately when the game starts, it can lead to an unfair advantage or accidental actions. Using `tcflush(STDIN_FILENO, TCIFLUSH)` after the countdown ensures the game starts with a clean slate.
**Action:** Always clear the input buffer with `tcflush` after a blocking countdown or transition period in interactive CLI applications to ensure intent-based interaction.

## 2026-05-24 - Live High Score as Gamification UX
**Learning:** In simple CLI games, providing live high-score tracking (e.g., "Score: 10 | High: 10") that updates the moment a record is broken creates immediate "micro-delight" and encourages continued engagement. Seeing the "High" value match the "Score" in real-time is a powerful psychological reward.
**Action:** Implement live-updating progress or record indicators in interactive CLI tools to provide immediate positive reinforcement.

## 2026-05-25 - DX as UX: Clean Builds for CI/CD
**Learning:** In a C++ project, the build process is the foundation of the Developer Experience. A broken build not only prevents local development but also paralyzes automated CI/CD workflows like CodeQL analysis. Resolving redundant logic and compilation errors (like duplicate variable declarations) is essential for maintaining a healthy project and enabling advanced security scanning.
**Action:** Prioritize clean, warning-free compilation and utilize helper functions to consolidate redundant logic, ensuring both developers and CI tools can interact with the codebase seamlessly.
## 2026-03-02 - Hiding the Cursor in CLI Games
**Learning:** In terminal applications that require rapid visual updates or where user input doesn't involve typing text, an actively blinking cursor can be a visual distraction. Hiding it during interaction (`\033[?25l`) and rigorously ensuring it is restored (`\033[?25h`) on exitβ€”including signal interruptsβ€”significantly improves the aesthetic and focus.
**Action:** Always hide the cursor for interactive CLI games and explicitly restore it across all exit paths, including async-signal-safe signal handlers.
3 changes: 2 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ env:

jobs:
build:
runs-on: ubuntu-latest
if: hashFiles('**/*.rs') != ''
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Build
Expand Down
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@
*.out
*.app
game
highscore.txt

# debug information files
*.dwo

# Game data
highscore.txt
32 changes: 0 additions & 32 deletions CODE_OF_CONDUCT.md

This file was deleted.

50 changes: 0 additions & 50 deletions CONTRIBUTING.md

This file was deleted.

76 changes: 0 additions & 76 deletions bitcoin_trading_simulaton.py

This file was deleted.

112 changes: 48 additions & 64 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include <iostream>
#include <fstream>
#include <chrono>
#include <thread>
#include <poll.h>
Expand All @@ -10,40 +9,27 @@
#include <cstdlib>

// Color and formatting macros for terminal output
#define CLR_SCORE "\033[1;36m" // Bold Cyan
#define CLR_HARD "\033[1;31m" // Bold Red
#define CLR_NORM "\033[1;32m" // Bold Green
#define CLR_CTRL "\033[1;33m" // Bold Yellow
#define RESET "\033[0m"
#define RED "\033[31m"
#define GREEN "\033[32m"
#define YELLOW "\033[33m"
#define BLUE "\033[34m"
#define CLR_SCORE "\033[1;36m"
#define CLR_HARD "\033[1;31m"
#define CLR_NORM "\033[1;32m"
#define CLR_CTRL "\033[1;33m"
#define CLR_RESET "\033[0m"
#define HIDE_CURSOR "\033[?25l"
#define SHOW_CURSOR "\033[?25h"

struct termios oldt;

void restore_terminal(int signum) {
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
// Use write() and _exit() because they are async-signal-safe
const char* msg = "\033[?25h\033[0m\n\nGame interrupted. Terminal settings restored.\n";
write(STDOUT_FILENO, msg, 58);
const char msg[] = "\033[0m\033[?25h\n\nGame interrupted. Terminal settings restored.\n";
write(STDOUT_FILENO, msg, sizeof(msg) - 1);
_exit(signum);
}

long long load_highscore() {
long long hs = 0;
std::ifstream f("highscore.txt");
if (f.is_open()) {
f >> hs;
}
return hs;
}

void save_highscore(long long hs) {
std::ofstream f("highscore.txt");
if (f.is_open()) {
f << hs;
}
}

int main() {
struct termios newt;
if (tcgetattr(STDIN_FILENO, &oldt) == -1) {
Expand All @@ -60,78 +46,76 @@ int main() {
return 1;
}

std::cout << HIDE_CURSOR;

long long highscore = load_highscore();
long long score = 0;
bool hardMode = false;
char input;
std::cout << "\033[?25l" << std::flush;

long long score = 0; bool hardMode = false; char input;
std::cout << CLR_CTRL << "==========================\n SPEED CLICKER\n==========================\n" << CLR_RESET
<< "Controls:\n " << CLR_CTRL << "[h]" << CLR_RESET << " Toggle Hard Mode (10x Speed!)\n "
<< CLR_CTRL << "[q]" << CLR_RESET << " Quit Game\n " << CLR_CTRL << "[Any key]" << CLR_RESET << " Click!\n\n";

if (highscore > 0) {
std::cout << CLR_SCORE << "Personal Best: " << highscore << CLR_RESET << "\n\n";
}

std::cout << CLR_CTRL << "Press any key to start... " << CLR_RESET << std::flush;
if (read(STDIN_FILENO, &input, 1) <= 0 || input == 'q') {
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
std::cout << SHOW_CURSOR << std::endl;
return 0;
std::cout << "Press any key to start... " << std::flush;
struct pollfd start_fds[1] = {{STDIN_FILENO, POLLIN, 0}};
if (poll(start_fds, 1, -1) > 0) {
if (read(STDIN_FILENO, &input, 1) > 0 && input == 'q') {
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
std::cout << "\033[?25h" << std::flush;
return 0;
}
}

struct pollfd fds[1] = {{STDIN_FILENO, POLLIN, 0}};
for (int i = 3; i > 0; --i) {
std::cout << "\r" << CLR_CTRL << "Starting in " << i << "... " << CLR_RESET << std::flush;
auto start = std::chrono::steady_clock::now();
while (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() < 1000) {
if (poll(fds, 1, 100) > 0) {
std::cout << "\rStarting in " << i << "... " << std::flush;
auto start_wait = std::chrono::steady_clock::now();
while (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_wait).count() < 1000) {
int elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_wait).count();
int remaining = std::max(0, 1000 - elapsed);
if (poll(start_fds, 1, std::min(remaining, 100)) > 0) {
if (read(STDIN_FILENO, &input, 1) > 0 && input == 'q') {
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
std::cout << SHOW_CURSOR << "\n" << std::flush;
std::cout << "\n\033[?25h" << std::flush;
return 0;
}
}
}
}
std::cout << "\r" << CLR_NORM << "GO! " << CLR_RESET << "\n" << std::flush;
std::cout << "\rGO! \n" << std::flush;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
tcflush(STDIN_FILENO, TCIFLUSH);

auto last_tick = std::chrono::steady_clock::now();
bool updateUI = true;

struct pollfd fds[1] = {{STDIN_FILENO, POLLIN, 0}};
while (true) {
if (poll(fds, 1, 0) > 0) {
int timeout_ms = hardMode ? 100 : 1000;
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_tick).count();
int remaining = std::max(0, static_cast<int>(timeout_ms - elapsed));

if (poll(fds, 1, remaining) > 0) {
if (read(STDIN_FILENO, &input, 1) <= 0 || input == 'q') break;
if (input == 'h') hardMode = !hardMode; else score++;
if (input == 'h') hardMode = !hardMode;
else score++;
updateUI = true;
}
auto now = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_tick).count() >= (hardMode ? 100 : 1000)) {
score++; last_tick = now; updateUI = true;

now = std::chrono::steady_clock::now();
elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_tick).count();
if (elapsed >= timeout_ms) {
score++;
last_tick = now;
updateUI = true;
}

if (updateUI) {
std::cout << "\r" << CLR_SCORE << "Score: " << score << CLR_RESET
<< " | High: " << (score > highscore ? score : highscore) << " "
std::cout << "\r" << CLR_SCORE << "Score: " << score << CLR_RESET << " "
<< (hardMode ? CLR_HARD "[HARD MODE]" : CLR_NORM "[NORMAL MODE]")
<< (score > highscore && highscore > 0 ? " NEW BEST! πŸ₯³" : "")
<< " " << std::flush;
updateUI = false;
}
}

tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
std::cout << "\n\n" << CLR_SCORE << "Final Score: " << score << CLR_RESET << "\n";

if (score > highscore) {
std::cout << CLR_NORM << "NEW HIGH SCORE! " << CLR_RESET << "Previous was " << highscore << "\n";
save_highscore(score);
}

std::cout << "Thanks for playing!" << SHOW_CURSOR << "\n";
std::cout << "\n\n" << CLR_SCORE << "Final Score: " << score << CLR_RESET << "\nThanks for playing!\n";
std::cout << "\033[?25h" << std::flush;
return 0;
}
Loading
Loading