feat(cmat): add cmake build for course material

Add a CMakeLists.txt to each exercise directory, included from
cmat/CMakeLists.txt, so every exercise and solution becomes a buildable
target. GUI exercises link the curses-based DOS_Unix implementations on
Unix-like systems; the Windows-specific DOS variants are not compiled.
Exercises without a portable path or with missing dependencies (Boost)
are reported as not available at configure time.

Targets are excluded from the default build and built by name, e.g.
`cmake --build build --target Ex11`, so intentionally buggy exercises
such as Ex21 do not break the default build.

Also fix two portability issues: rename Ex11bug/pole.h to Pole.h for
case-sensitive filesystems and drop the extra Light:: qualification in
SolutionEx71A/Light.h. Document build commands and layout in AGENTS.md.

Co-authored-by: Qwen 3.8 27B <qwen3.8_27b-q3_k_xl@lmstudio.local>
This commit is contained in:
2026-10-05 11:55:00 +02:00
co-authored by Qwen 3.8 27B
parent e8241fd1ff
commit e09540c2b2
701 changed files with 27282 additions and 16 deletions
+51
View File
@@ -0,0 +1,51 @@
#include "CursesObj.h"
#include "curses.h"
CursesObj::CursesObj() {
// Colours 0 to 7 are defined by curses. But we need grey as well
// Define a suitable constant here (and create the colour later)
const int COLOR_GREY = 8;
initscr(); /* initialize the curses library */
nonl(); /* tell curses not to do NL->CR/NL on output */
cbreak(); /* take input chars one at a time, no wait for \n */
nodelay(stdscr, FALSE);
noecho(); /* echo input - in color */
curs_set(0);
intrflush(stdscr, FALSE);
if (has_colors()) {
start_color();
/*
* Simple color assignment, often all we need. Color pair 0 cannot
* be redefined. This example uses the same value for the color
* pair as for the foreground color, though of course that is not
* necessary:
*/
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_CYAN, COLOR_BLACK);
init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
init_pair(7, COLOR_WHITE, COLOR_BLACK);
// Create 'grey' and then use it as foreground colour
init_color(8, 1, 333, 333);
init_pair(8, COLOR_GREY, COLOR_BLACK);
}
}
CursesObj::~CursesObj() {
clear();
refresh();
endwin();
}
CursesObj &CursesObj::Instance() {
static CursesObj instance;
return instance;
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef CURSESOBJ_H
#define CURSESOBJ_H
class CursesObj {
private:
CursesObj();
~CursesObj();
public:
static CursesObj &Instance();
};
#endif
+13
View File
@@ -0,0 +1,13 @@
#ifndef KEYBOARD_H
#define KEYBOARD_H
class Keyboard {
public:
Keyboard();
bool isCharAvail();
char getChar();
private:
};
#endif
+13
View File
@@ -0,0 +1,13 @@
#include "Keyboard.h"
#include <conio.h>
Keyboard::Keyboard() {}
bool Keyboard::isCharAvail() {
// 'return kbhit()' is sufficient, but Microsoft
// issues a warning 'coercing int to bool' and encourages
// the use of '!=0' to eliminate it
return _kbhit() != 0;
}
char Keyboard::getChar() { return _getch(); }
+28
View File
@@ -0,0 +1,28 @@
#include "Keyboard.h"
#include "CursesObj.h"
#include "curses.h"
Keyboard::Keyboard() {
CursesObj::Instance(); // Force initialization
nodelay(stdscr, FALSE); // Default to block on getch
}
bool Keyboard::isCharAvail() {
nodelay(stdscr, TRUE); // getch must not block for this polling
int c; // DON'T make this 'char'; 'ERR' may be an 'int' - on some systems,
// just like 'EOF'
bool charWaiting = (c = getch()) != ERR; // If getch returns ERR, no char
// waiting, otherwise, next char is
// returned
if (charWaiting)
ungetch(c); // We got a char; put it back! (We just want to know if there
// is one waiting.)
return charWaiting;
}
char Keyboard::getChar() { return getch(); }
+19
View File
@@ -0,0 +1,19 @@
#ifndef SCREEN_H
#define SCREEN_H
#include <string>
namespace Screen {
const int BLACK = 0;
const int GRAY = 8; // FOREGROUND_INTENSITY;
const int BLUE = 9; // FOREGROUND_RED|FOREGROUND_INTENSITY
const int GREEN = 10; // FOREGROUND_GREEN|FOREGROUND_INTENSITY;
const int RED = 12; // FOREGROUND_RED|FOREGROUND_INTENSITY
const int YELLOW = RED | GREEN;
const int WHITE = RED | GREEN | BLUE;
const int BACKGROUND = 128; // OR (|) with the color value for gray background
void write(std::string s, int x, int y, int color);
};
#endif
+32
View File
@@ -0,0 +1,32 @@
#include <conio.h>
#include <string>
#include <windows.h>
using std::string;
#include "Screen.h"
namespace {
HANDLE hStdout = 0;
}
void Screen::write(string s, int x, int y, int color) {
if (hStdout == 0) {
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
}
COORD screenPos;
screenPos.X = x;
screenPos.Y = y;
unsigned long ln;
static unsigned short attrib[80];
for (unsigned int i = 0; i < s.length(); ++i) {
attrib[i] = color;
}
WriteConsoleOutputAttribute(hStdout, attrib, s.length(), screenPos, &ln);
WriteConsoleOutputCharacter(hStdout, s.c_str(), s.length(), screenPos, &ln);
}
+66
View File
@@ -0,0 +1,66 @@
#include "Screen.h"
#include "CursesObj.h"
#include "curses.h"
#include <stdio.h>
#include <string.h>
// Forward declarations
int ConvertColour(int);
void Screen::write(std::string s, int x, int y, int color) {
CursesObj::Instance(); // Force initialization
if (color >= 128)
color -= 128; // Remove gray background - does not work on Linux
std::string blank_str(s.length(), ' ');
mvaddstr(y, x, blank_str.c_str()); // Always draw a black
refresh();
if (color != Screen::BLACK) // If colour is not black, also draw the color
{
printf("\e[%i;3%im", 0, ConvertColour(color));
fflush(stdout);
mvaddstr(y, x, s.c_str());
refresh();
}
}
typedef struct {
int pelio_colour;
int curses_color_pair;
} Entry;
// Entry null pointer
const Entry *ENTRY_NP = (Entry *)0;
typedef Entry Table[];
Table ColourTable = {{Screen::GRAY, 8}, // Display as white
{Screen::BLUE, 4}, {Screen::GREEN, 2}, {Screen::RED, 1}, {Screen::YELLOW, 3}, {Screen::WHITE, 7}};
const int LEN_TAB = sizeof ColourTable / sizeof ColourTable[0];
const Entry *TabSearch(Table t, int nEntries, int colour) {
Entry *p;
for (p = t; p < t + nEntries && p->pelio_colour != colour; p++)
; // EMPTY BODY
return (p < t + nEntries) ? p : ENTRY_NP;
}
int ConvertColour(int colour) {
int result = 0;
const Entry *p;
p = TabSearch(ColourTable, LEN_TAB, colour); // Search the table for the colour specified
if (p != ENTRY_NP) // If the colour is in the table ...
result = p->curses_color_pair; // ... return the 'int' for the associated
// curses colour pair
return result;
}