Merge commit '26639c0c2833fa0fd7a468e6e4679327ea79beff'
This commit is contained in:
@@ -10,7 +10,7 @@ set(CMAKE_CXX_EXTENSIONS OFF)
|
|||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
option(ND_WFC_BUILD_TESTS "Build tests" OFF) # Temporarily disabled due to gtest hash issue
|
option(ND_WFC_BUILD_TESTS "Build tests" ON)
|
||||||
option(ND_WFC_BUILD_EXAMPLES "Build examples" ON)
|
option(ND_WFC_BUILD_EXAMPLES "Build examples" ON)
|
||||||
option(ND_WFC_USE_SYSTEM_LIBS "Use system libraries instead of bundled" OFF)
|
option(ND_WFC_USE_SYSTEM_LIBS "Use system libraries instead of bundled" OFF)
|
||||||
|
|
||||||
|
|||||||
21
demos/CMakeLists.txt
Normal file
21
demos/CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Console Renderer Demo
|
||||||
|
add_executable(sudoku_demo_renderer
|
||||||
|
sudoku_demo_renderer.cpp
|
||||||
|
console_renderer.cpp
|
||||||
|
sudoku/sudoku.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(sudoku_demo_renderer PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||||
|
)
|
||||||
|
|
||||||
|
# Link pthread for std::thread
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
target_link_libraries(sudoku_demo_renderer Threads::Threads)
|
||||||
|
|
||||||
|
# Set C++17 standard
|
||||||
|
set_target_properties(sudoku_demo_renderer PROPERTIES
|
||||||
|
CXX_STANDARD 17
|
||||||
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
)
|
||||||
267
demos/README.md
Normal file
267
demos/README.md
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
# Console Rendering System for Puzzle Demos
|
||||||
|
|
||||||
|
This directory contains a comprehensive console rendering system designed for real-time puzzle visualization without adding new lines to the console. The system is perfect for showing solving animations and interactive demos.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Real-time Updates**: Render puzzle states without scrolling the console
|
||||||
|
- **Space Pre-allocation**: Reserve console space before rendering to prevent layout issues
|
||||||
|
- **Animation Support**: Smooth animations for solving steps and cell highlights
|
||||||
|
- **Cross-platform**: Works on Linux, macOS, and Windows
|
||||||
|
- **Multiple Puzzle Types**: Support for Sudoku and Nonogram puzzles
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Basic Sudoku Rendering
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include "console_renderer.h"
|
||||||
|
#include "sudoku/sudoku.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// 1. Create a Sudoku puzzle
|
||||||
|
std::string puzzle = "530070000600195000098000060800060003400803001700020006060000280000419005000080079";
|
||||||
|
Sudoku sudoku(puzzle);
|
||||||
|
|
||||||
|
// 2. Create renderer
|
||||||
|
SudokuRenderer renderer(sudoku);
|
||||||
|
|
||||||
|
// 3. IMPORTANT: Allocate space first!
|
||||||
|
renderer.allocateSpace();
|
||||||
|
|
||||||
|
// 4. Render initial state
|
||||||
|
renderer.render();
|
||||||
|
|
||||||
|
// 5. Update puzzle and re-render (updates in place!)
|
||||||
|
sudoku.set(0, 0, 5);
|
||||||
|
renderer.render();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Animation Example
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Animate a cell change with highlighting
|
||||||
|
renderer.animateCell(row, col, new_value, 200); // 200ms delay
|
||||||
|
|
||||||
|
// Show solving progress
|
||||||
|
renderer.showSolvingProgress("Solving step 1 of 10");
|
||||||
|
|
||||||
|
// Highlight specific cells
|
||||||
|
renderer.renderWithHighlight(highlight_row, highlight_col);
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### SudokuRenderer
|
||||||
|
|
||||||
|
The `SudokuRenderer` class provides a clean API for rendering Sudoku puzzles with real-time updates.
|
||||||
|
|
||||||
|
#### Constructor
|
||||||
|
```cpp
|
||||||
|
SudokuRenderer(const Sudoku& sudoku)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Core Methods
|
||||||
|
```cpp
|
||||||
|
void allocateSpace() // Reserve console space (call first!)
|
||||||
|
void render() // Render current puzzle state
|
||||||
|
void clear() // Clear allocated space
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Animation Methods
|
||||||
|
```cpp
|
||||||
|
void renderWithHighlight(int row, int col) // Highlight specific cell
|
||||||
|
void showSolvingProgress(const std::string& status) // Update status line
|
||||||
|
void animateCell(int row, int col, uint8_t value, int delay_ms = 100) // Animate cell change
|
||||||
|
```
|
||||||
|
|
||||||
|
### NonogramRenderer
|
||||||
|
|
||||||
|
The `NonogramRenderer` class handles Nonogram puzzle visualization with hints and solution grid.
|
||||||
|
|
||||||
|
#### Constructor
|
||||||
|
```cpp
|
||||||
|
NonogramRenderer(const Nonogram& nonogram)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Core Methods
|
||||||
|
```cpp
|
||||||
|
void allocateSpace() // Reserve console space
|
||||||
|
void render() // Render puzzle with hints
|
||||||
|
void clear() // Clear allocated space
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Nonogram-specific Methods
|
||||||
|
```cpp
|
||||||
|
void renderWithState(const std::vector<std::vector<int>>& state) // Render with current solving state
|
||||||
|
void showSolvingProgress(const std::string& status) // Update status
|
||||||
|
```
|
||||||
|
|
||||||
|
### ConsoleRenderer (Base Class)
|
||||||
|
|
||||||
|
Provides low-level console control utilities.
|
||||||
|
|
||||||
|
#### Static Utilities
|
||||||
|
```cpp
|
||||||
|
static void moveCursorUp(int lines)
|
||||||
|
static void moveCursorDown(int lines)
|
||||||
|
static void moveCursorToPosition(int row, int col)
|
||||||
|
static void clearLine()
|
||||||
|
static void clearScreen()
|
||||||
|
static void hideCursor()
|
||||||
|
static void showCursor()
|
||||||
|
static void sleep(int milliseconds)
|
||||||
|
static std::string repeatChar(char c, int count)
|
||||||
|
static std::string centerText(const std::string& text, int width)
|
||||||
|
```
|
||||||
|
|
||||||
|
### DemoAnimator
|
||||||
|
|
||||||
|
Helper class for creating smooth demo animations.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
static void typewriterEffect(const std::string& text, int delay_ms = 50)
|
||||||
|
static void fadeIn(const std::vector<std::string>& lines, int delay_ms = 100)
|
||||||
|
static void progressBar(const std::string& label, int current, int total, int width = 30)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Usage Notes
|
||||||
|
|
||||||
|
### 1. Always Allocate Space First
|
||||||
|
|
||||||
|
**Critical**: Call `allocateSpace()` before any rendering operations:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
SudokuRenderer renderer(sudoku);
|
||||||
|
renderer.allocateSpace(); // This MUST be called first!
|
||||||
|
renderer.render();
|
||||||
|
```
|
||||||
|
|
||||||
|
This reserves the necessary console lines and prevents the display from being corrupted by new output.
|
||||||
|
|
||||||
|
### 2. Real-time Updates
|
||||||
|
|
||||||
|
After calling `allocateSpace()`, all subsequent `render()` calls will update the display in place:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
renderer.allocateSpace(); // Once at the start
|
||||||
|
renderer.render(); // Initial display
|
||||||
|
|
||||||
|
// Update puzzle state
|
||||||
|
sudoku.set(0, 0, 5);
|
||||||
|
renderer.render(); // Updates in place - no new lines!
|
||||||
|
|
||||||
|
sudoku.set(1, 1, 3);
|
||||||
|
renderer.render(); // Updates in place again
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Animation Best Practices
|
||||||
|
|
||||||
|
For smooth animations, use the provided animation methods:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Good: Use built-in animation
|
||||||
|
renderer.animateCell(row, col, value, 200);
|
||||||
|
|
||||||
|
// Also good: Manual animation with proper timing
|
||||||
|
for (int i = 0; i < steps.size(); ++i) {
|
||||||
|
renderer.showSolvingProgress("Step " + std::to_string(i + 1));
|
||||||
|
// Update state
|
||||||
|
renderer.render();
|
||||||
|
ConsoleRenderer::sleep(300);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Error Handling
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
try {
|
||||||
|
ConsoleRenderer::hideCursor(); // For cleaner display
|
||||||
|
|
||||||
|
SudokuRenderer renderer(sudoku);
|
||||||
|
renderer.allocateSpace();
|
||||||
|
renderer.render();
|
||||||
|
|
||||||
|
// ... your demo logic ...
|
||||||
|
|
||||||
|
ConsoleRenderer::showCursor(); // Always restore cursor
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
ConsoleRenderer::showCursor(); // Restore cursor on error
|
||||||
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
### Using CMake
|
||||||
|
```bash
|
||||||
|
cd /testbed/demos
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake ..
|
||||||
|
make sudoku_demo_renderer
|
||||||
|
./sudoku_demo_renderer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Compilation
|
||||||
|
```bash
|
||||||
|
cd /testbed/demos
|
||||||
|
g++ -std=c++17 -I. -I.. -pthread \
|
||||||
|
sudoku_demo_renderer.cpp console_renderer.cpp sudoku/sudoku.cpp \
|
||||||
|
-o sudoku_demo_renderer
|
||||||
|
./sudoku_demo_renderer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Demo Programs
|
||||||
|
|
||||||
|
### sudoku_demo_renderer
|
||||||
|
A comprehensive demonstration showing:
|
||||||
|
- Basic Sudoku rendering
|
||||||
|
- Real-time puzzle updates
|
||||||
|
- Solving animations
|
||||||
|
- API usage examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./sudoku_demo_renderer
|
||||||
|
```
|
||||||
|
|
||||||
|
### test_renderer
|
||||||
|
Simple test program to verify the renderer functionality:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./test_renderer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Console Control
|
||||||
|
|
||||||
|
The system uses ANSI escape sequences for cursor control:
|
||||||
|
- `\033[nA` - Move cursor up n lines
|
||||||
|
- `\033[nB` - Move cursor down n lines
|
||||||
|
- `\033[2K` - Clear current line
|
||||||
|
- `\033[?25l` - Hide cursor
|
||||||
|
- `\033[?25h` - Show cursor
|
||||||
|
|
||||||
|
### Memory Efficiency
|
||||||
|
|
||||||
|
- Sudoku puzzles use only 41 bytes of storage
|
||||||
|
- Nonogram puzzles use bit-packed storage for solutions
|
||||||
|
- Renderers don't store puzzle state - they reference existing objects
|
||||||
|
|
||||||
|
### Cross-platform Support
|
||||||
|
|
||||||
|
The renderer works on:
|
||||||
|
- Linux (tested)
|
||||||
|
- macOS (ANSI escape sequences supported)
|
||||||
|
- Windows (with ANSI support enabled)
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
See the demo programs for complete working examples:
|
||||||
|
- `sudoku_demo_renderer.cpp` - Full-featured Sudoku demo
|
||||||
|
- `test_renderer.cpp` - Basic functionality test
|
||||||
|
|
||||||
|
The console rendering system provides a powerful foundation for creating engaging puzzle demos and interactive solving visualizations!
|
||||||
509
demos/console_renderer.cpp
Normal file
509
demos/console_renderer.cpp
Normal file
@@ -0,0 +1,509 @@
|
|||||||
|
#include "console_renderer.h"
|
||||||
|
#include "sudoku/sudoku.h"
|
||||||
|
#include "nonogram/nonogram.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
// ANSI escape codes for cursor control
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#include <conio.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// ConsoleRenderer Implementation
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
void ConsoleRenderer::moveCursorUp(int lines) {
|
||||||
|
if (lines > 0) {
|
||||||
|
std::cout << "\033[" << lines << "A";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleRenderer::moveCursorDown(int lines) {
|
||||||
|
if (lines > 0) {
|
||||||
|
std::cout << "\033[" << lines << "B";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleRenderer::moveCursorToColumn(int col) {
|
||||||
|
std::cout << "\033[" << col << "G";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleRenderer::moveCursorToPosition(int row, int col) {
|
||||||
|
std::cout << "\033[" << row << ";" << col << "H";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleRenderer::clearLine() {
|
||||||
|
std::cout << "\033[2K";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleRenderer::clearScreen() {
|
||||||
|
std::cout << "\033[2J\033[H";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleRenderer::hideCursor() {
|
||||||
|
std::cout << "\033[?25l";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleRenderer::showCursor() {
|
||||||
|
std::cout << "\033[?25h";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleRenderer::sleep(int milliseconds) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ConsoleRenderer::repeatChar(char c, int count) {
|
||||||
|
return std::string(count, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ConsoleRenderer::centerText(const std::string& text, int width) {
|
||||||
|
if (static_cast<int>(text.length()) >= width) return text;
|
||||||
|
int padding = (width - static_cast<int>(text.length())) / 2;
|
||||||
|
return repeatChar(' ', padding) + text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// SudokuRenderer Implementation
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
SudokuRenderer::SudokuRenderer(const Sudoku& sudoku) : sudoku_(sudoku) {}
|
||||||
|
|
||||||
|
void SudokuRenderer::allocateSpace() {
|
||||||
|
if (space_allocated_) return;
|
||||||
|
|
||||||
|
// Print empty lines to reserve space
|
||||||
|
for (int i = 0; i < TOTAL_HEIGHT; ++i) {
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocated_lines_ = TOTAL_HEIGHT;
|
||||||
|
space_allocated_ = true;
|
||||||
|
|
||||||
|
// Move cursor back to start of allocated space
|
||||||
|
moveCursorUp(TOTAL_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SudokuRenderer::render() {
|
||||||
|
if (!space_allocated_) {
|
||||||
|
allocateSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save cursor position
|
||||||
|
std::cout << "\033[s";
|
||||||
|
|
||||||
|
// Render title
|
||||||
|
std::string title = "SUDOKU PUZZLE";
|
||||||
|
std::cout << centerText(title, GRID_WIDTH) << std::endl;
|
||||||
|
std::cout << centerText(repeatChar('=', title.length()), GRID_WIDTH) << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Render grid
|
||||||
|
for (int row = 0; row < 9; ++row) {
|
||||||
|
std::cout << formatSudokuLine(row) << std::endl;
|
||||||
|
|
||||||
|
// Add separator lines after rows 2 and 5
|
||||||
|
if (row == 2 || row == 5) {
|
||||||
|
std::cout << getSeparatorLine() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Render status
|
||||||
|
std::string status = sudoku_.isSolved() ? "SOLVED!" :
|
||||||
|
sudoku_.isValid() ? "Valid puzzle" : "Invalid puzzle";
|
||||||
|
std::cout << centerText(status, GRID_WIDTH) << std::endl;
|
||||||
|
|
||||||
|
// Restore cursor position
|
||||||
|
std::cout << "\033[u";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SudokuRenderer::clear() {
|
||||||
|
if (!space_allocated_) return;
|
||||||
|
|
||||||
|
// Move to start of allocated space
|
||||||
|
std::cout << "\033[s";
|
||||||
|
|
||||||
|
// Clear all lines
|
||||||
|
for (int i = 0; i < allocated_lines_; ++i) {
|
||||||
|
clearLine();
|
||||||
|
if (i < allocated_lines_ - 1) {
|
||||||
|
moveCursorDown(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore cursor position
|
||||||
|
std::cout << "\033[u";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SudokuRenderer::renderWithHighlight(int highlight_row, int highlight_col) {
|
||||||
|
if (!space_allocated_) {
|
||||||
|
allocateSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save cursor position
|
||||||
|
std::cout << "\033[s";
|
||||||
|
|
||||||
|
// Render title
|
||||||
|
std::string title = "SUDOKU PUZZLE";
|
||||||
|
std::cout << centerText(title, GRID_WIDTH) << std::endl;
|
||||||
|
std::cout << centerText(repeatChar('=', title.length()), GRID_WIDTH) << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Render grid with highlighting
|
||||||
|
for (int row = 0; row < 9; ++row) {
|
||||||
|
std::string line = formatSudokuLine(row);
|
||||||
|
|
||||||
|
// Apply highlighting if this is the target row
|
||||||
|
if (row == highlight_row && highlight_col >= 0 && highlight_col < 9) {
|
||||||
|
// Calculate position of highlighted cell in the line
|
||||||
|
int pos = highlight_col * 2; // Each cell takes 2 chars ("X ")
|
||||||
|
if (highlight_col >= 3) pos += 2; // Account for " | " separator
|
||||||
|
if (highlight_col >= 6) pos += 2; // Account for second " | " separator
|
||||||
|
|
||||||
|
// Insert highlighting safely
|
||||||
|
std::string highlighted = line;
|
||||||
|
if (pos >= 0 && pos < static_cast<int>(highlighted.length())) {
|
||||||
|
// Insert reverse video before the character
|
||||||
|
highlighted.insert(pos, "\033[7m");
|
||||||
|
// Insert reset after the character (accounting for the inserted escape sequence)
|
||||||
|
if (pos + 5 < static_cast<int>(highlighted.length())) {
|
||||||
|
highlighted.insert(pos + 5, "\033[0m");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line = highlighted;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << line << std::endl;
|
||||||
|
|
||||||
|
// Add separator lines after rows 2 and 5
|
||||||
|
if (row == 2 || row == 5) {
|
||||||
|
std::cout << getSeparatorLine() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Render status
|
||||||
|
std::string status = sudoku_.isSolved() ? "SOLVED!" :
|
||||||
|
sudoku_.isValid() ? "Valid puzzle" : "Invalid puzzle";
|
||||||
|
std::cout << centerText(status, GRID_WIDTH) << std::endl;
|
||||||
|
|
||||||
|
// Restore cursor position
|
||||||
|
std::cout << "\033[u";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SudokuRenderer::showSolvingProgress(const std::string& status) {
|
||||||
|
if (!space_allocated_) return;
|
||||||
|
|
||||||
|
// Move to status line (last line of allocated space)
|
||||||
|
std::cout << "\033[s";
|
||||||
|
moveCursorDown(allocated_lines_ - 1);
|
||||||
|
clearLine();
|
||||||
|
|
||||||
|
std::string display_status = status.empty() ? "Solving..." : status;
|
||||||
|
std::cout << centerText(display_status, GRID_WIDTH);
|
||||||
|
|
||||||
|
std::cout << "\033[u";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SudokuRenderer::animateCell(int row, int col, uint8_t value, int delay_ms) {
|
||||||
|
// Highlight the cell
|
||||||
|
renderWithHighlight(row, col);
|
||||||
|
sleep(delay_ms);
|
||||||
|
|
||||||
|
// Render normally
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SudokuRenderer::formatSudokuLine(int row) const {
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
for (int col = 0; col < 9; ++col) {
|
||||||
|
uint8_t value = sudoku_.get(row, col);
|
||||||
|
oss << getCellChar(value);
|
||||||
|
|
||||||
|
if (col < 8) {
|
||||||
|
oss << " ";
|
||||||
|
// Add vertical separator after columns 2 and 5
|
||||||
|
if (col == 2 || col == 5) {
|
||||||
|
oss << "| ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SudokuRenderer::getSeparatorLine() const {
|
||||||
|
return "------+-------+------";
|
||||||
|
}
|
||||||
|
|
||||||
|
char SudokuRenderer::getCellChar(uint8_t value) const {
|
||||||
|
return (value == 0) ? '.' : static_cast<char>('0' + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// NonogramRenderer Implementation
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
NonogramRenderer::NonogramRenderer(const Nonogram& nonogram) : nonogram_(nonogram) {}
|
||||||
|
|
||||||
|
void NonogramRenderer::allocateSpace() {
|
||||||
|
if (space_allocated_) return;
|
||||||
|
|
||||||
|
int height = calculateHeight();
|
||||||
|
|
||||||
|
// Print empty lines to reserve space
|
||||||
|
for (int i = 0; i < height; ++i) {
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocated_lines_ = height;
|
||||||
|
space_allocated_ = true;
|
||||||
|
|
||||||
|
// Move cursor back to start of allocated space
|
||||||
|
moveCursorUp(height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NonogramRenderer::render() {
|
||||||
|
if (!space_allocated_) {
|
||||||
|
allocateSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save cursor position
|
||||||
|
std::cout << "\033[s";
|
||||||
|
|
||||||
|
// Render title
|
||||||
|
std::string title = "NONOGRAM PUZZLE";
|
||||||
|
int width = calculateWidth();
|
||||||
|
std::cout << centerText(title, width) << std::endl;
|
||||||
|
std::cout << centerText(repeatChar('=', title.length()), width) << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Render column hints
|
||||||
|
std::cout << formatColumnHints() << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Render grid with row hints
|
||||||
|
for (size_t row = 0; row < nonogram_.getHeight(); ++row) {
|
||||||
|
std::cout << formatNonogramLine(static_cast<int>(row)) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Render status
|
||||||
|
std::string status = "Nonogram loaded";
|
||||||
|
std::cout << centerText(status, width) << std::endl;
|
||||||
|
|
||||||
|
// Restore cursor position
|
||||||
|
std::cout << "\033[u";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NonogramRenderer::clear() {
|
||||||
|
if (!space_allocated_) return;
|
||||||
|
|
||||||
|
// Move to start of allocated space
|
||||||
|
std::cout << "\033[s";
|
||||||
|
|
||||||
|
// Clear all lines
|
||||||
|
for (int i = 0; i < allocated_lines_; ++i) {
|
||||||
|
clearLine();
|
||||||
|
if (i < allocated_lines_ - 1) {
|
||||||
|
moveCursorDown(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore cursor position
|
||||||
|
std::cout << "\033[u";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NonogramRenderer::renderWithState(const std::vector<std::vector<int>>& state) {
|
||||||
|
if (!space_allocated_) {
|
||||||
|
allocateSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save cursor position
|
||||||
|
std::cout << "\033[s";
|
||||||
|
|
||||||
|
// Render title
|
||||||
|
std::string title = "NONOGRAM PUZZLE";
|
||||||
|
int width = calculateWidth();
|
||||||
|
std::cout << centerText(title, width) << std::endl;
|
||||||
|
std::cout << centerText(repeatChar('=', title.length()), width) << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Render column hints
|
||||||
|
std::cout << formatColumnHints() << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Render grid with current state
|
||||||
|
for (size_t row = 0; row < nonogram_.getHeight(); ++row) {
|
||||||
|
std::cout << formatNonogramLine(static_cast<int>(row), &state) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Render status
|
||||||
|
std::string status = "Solving...";
|
||||||
|
std::cout << centerText(status, width) << std::endl;
|
||||||
|
|
||||||
|
// Restore cursor position
|
||||||
|
std::cout << "\033[u";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NonogramRenderer::showSolvingProgress(const std::string& status) {
|
||||||
|
if (!space_allocated_) return;
|
||||||
|
|
||||||
|
// Move to status line
|
||||||
|
std::cout << "\033[s";
|
||||||
|
moveCursorDown(allocated_lines_ - 1);
|
||||||
|
clearLine();
|
||||||
|
|
||||||
|
std::string display_status = status.empty() ? "Solving..." : status;
|
||||||
|
std::cout << centerText(display_status, calculateWidth());
|
||||||
|
|
||||||
|
std::cout << "\033[u";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NonogramRenderer::formatNonogramLine(int row, const std::vector<std::vector<int>>* state) const {
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
// Add row hints (right-aligned in a fixed width)
|
||||||
|
auto hints = nonogram_.getRowHints(row);
|
||||||
|
std::ostringstream hints_oss;
|
||||||
|
for (size_t i = 0; i < hints.size(); ++i) {
|
||||||
|
if (i > 0) hints_oss << " ";
|
||||||
|
hints_oss << static_cast<int>(hints[i]);
|
||||||
|
}
|
||||||
|
std::string hints_str = hints_oss.str();
|
||||||
|
|
||||||
|
// Right-align hints in 8 character field
|
||||||
|
oss << std::setw(8) << hints_str << " | ";
|
||||||
|
|
||||||
|
// Add grid cells
|
||||||
|
for (size_t col = 0; col < nonogram_.getWidth(); ++col) {
|
||||||
|
char cell_char = '?';
|
||||||
|
|
||||||
|
if (state && row < static_cast<int>(state->size()) && col < state->at(row).size()) {
|
||||||
|
int cell_state = state->at(row)[col];
|
||||||
|
if (cell_state == 1) cell_char = '#';
|
||||||
|
else if (cell_state == 0) cell_char = '.';
|
||||||
|
} else if (nonogram_.hasSolution()) {
|
||||||
|
cell_char = nonogram_.getSolutionCell(row, col) ? '#' : '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << cell_char;
|
||||||
|
if (col < nonogram_.getWidth() - 1) oss << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NonogramRenderer::formatColumnHints() const {
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
// Find max hint height for columns
|
||||||
|
int max_hint_height = 0;
|
||||||
|
for (size_t col = 0; col < nonogram_.getColumnCount(); ++col) {
|
||||||
|
auto hints = nonogram_.getColumnHints(col);
|
||||||
|
max_hint_height = std::max(max_hint_height, static_cast<int>(hints.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render column hints from top to bottom
|
||||||
|
for (int hint_row = 0; hint_row < max_hint_height; ++hint_row) {
|
||||||
|
oss << std::setw(8) << " " << " | "; // Space for row hints
|
||||||
|
|
||||||
|
for (size_t col = 0; col < nonogram_.getColumnCount(); ++col) {
|
||||||
|
auto hints = nonogram_.getColumnHints(col);
|
||||||
|
|
||||||
|
// Calculate which hint to show (from top)
|
||||||
|
int hint_index = hint_row - (max_hint_height - static_cast<int>(hints.size()));
|
||||||
|
|
||||||
|
if (hint_index >= 0 && hint_index < static_cast<int>(hints.size())) {
|
||||||
|
oss << static_cast<int>(hints[hint_index]);
|
||||||
|
} else {
|
||||||
|
oss << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col < nonogram_.getColumnCount() - 1) oss << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hint_row < max_hint_height - 1) oss << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
int NonogramRenderer::calculateWidth() const {
|
||||||
|
return 10 + static_cast<int>(nonogram_.getWidth()) * 2; // hints + grid
|
||||||
|
}
|
||||||
|
|
||||||
|
int NonogramRenderer::calculateHeight() const {
|
||||||
|
// Title (2) + column hints (variable) + grid + status (3)
|
||||||
|
int max_col_hints = 0;
|
||||||
|
for (size_t col = 0; col < nonogram_.getColumnCount(); ++col) {
|
||||||
|
auto hints = nonogram_.getColumnHints(col);
|
||||||
|
max_col_hints = std::max(max_col_hints, static_cast<int>(hints.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2 + max_col_hints + 1 + static_cast<int>(nonogram_.getHeight()) + 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// DemoAnimator Implementation
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
void DemoAnimator::typewriterEffect(const std::string& text, int delay_ms) {
|
||||||
|
for (char c : text) {
|
||||||
|
std::cout << c;
|
||||||
|
std::cout.flush();
|
||||||
|
ConsoleRenderer::sleep(delay_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DemoAnimator::fadeIn(const std::vector<std::string>& lines, int delay_ms) {
|
||||||
|
for (const auto& line : lines) {
|
||||||
|
std::cout << line << std::endl;
|
||||||
|
ConsoleRenderer::sleep(delay_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DemoAnimator::progressBar(const std::string& label, int current, int total, int width) {
|
||||||
|
double percentage = static_cast<double>(current) / total;
|
||||||
|
int filled = static_cast<int>(percentage * width);
|
||||||
|
|
||||||
|
std::cout << "\r" << label << " [";
|
||||||
|
for (int i = 0; i < width; ++i) {
|
||||||
|
if (i < filled) {
|
||||||
|
std::cout << "=";
|
||||||
|
} else if (i == filled) {
|
||||||
|
std::cout << ">";
|
||||||
|
} else {
|
||||||
|
std::cout << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "] " << static_cast<int>(percentage * 100) << "%";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
130
demos/console_renderer.h
Normal file
130
demos/console_renderer.h
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base Console Renderer
|
||||||
|
*
|
||||||
|
* Provides utilities for console-based rendering with cursor control,
|
||||||
|
* allowing for real-time updates without adding new lines.
|
||||||
|
*
|
||||||
|
* Key features:
|
||||||
|
* - Allocate screen space before rendering
|
||||||
|
* - Move cursor to update specific areas
|
||||||
|
* - Clear and redraw content in place
|
||||||
|
* - Cross-platform cursor control
|
||||||
|
*/
|
||||||
|
class ConsoleRenderer {
|
||||||
|
public:
|
||||||
|
ConsoleRenderer() = default;
|
||||||
|
virtual ~ConsoleRenderer() = default;
|
||||||
|
|
||||||
|
// Core rendering interface
|
||||||
|
virtual void allocateSpace() = 0;
|
||||||
|
virtual void render() = 0;
|
||||||
|
virtual void clear() = 0;
|
||||||
|
|
||||||
|
// Cursor control utilities
|
||||||
|
static void moveCursorUp(int lines);
|
||||||
|
static void moveCursorDown(int lines);
|
||||||
|
static void moveCursorToColumn(int col);
|
||||||
|
static void moveCursorToPosition(int row, int col);
|
||||||
|
static void clearLine();
|
||||||
|
static void clearScreen();
|
||||||
|
static void hideCursor();
|
||||||
|
static void showCursor();
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
static void sleep(int milliseconds);
|
||||||
|
static std::string repeatChar(char c, int count);
|
||||||
|
static std::string centerText(const std::string& text, int width);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int allocated_lines_ = 0;
|
||||||
|
bool space_allocated_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sudoku Console Renderer
|
||||||
|
*
|
||||||
|
* Renders Sudoku puzzles with proper grid formatting and supports
|
||||||
|
* real-time updates for solving animations.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* SudokuRenderer renderer(sudoku);
|
||||||
|
* renderer.allocateSpace(); // Reserve console space
|
||||||
|
* renderer.render(); // Initial render
|
||||||
|
*
|
||||||
|
* // Update sudoku state and re-render
|
||||||
|
* sudoku.set(0, 0, 5);
|
||||||
|
* renderer.render(); // Updates in place
|
||||||
|
*/
|
||||||
|
class SudokuRenderer : public ConsoleRenderer {
|
||||||
|
public:
|
||||||
|
explicit SudokuRenderer(const class Sudoku& sudoku);
|
||||||
|
|
||||||
|
void allocateSpace() override;
|
||||||
|
void render() override;
|
||||||
|
void clear() override;
|
||||||
|
|
||||||
|
// Sudoku-specific methods
|
||||||
|
void renderWithHighlight(int highlight_row = -1, int highlight_col = -1);
|
||||||
|
void showSolvingProgress(const std::string& status = "");
|
||||||
|
|
||||||
|
// Animation support
|
||||||
|
void animateCell(int row, int col, uint8_t value, int delay_ms = 100);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const class Sudoku& sudoku_;
|
||||||
|
|
||||||
|
// Rendering helpers
|
||||||
|
std::string formatSudokuLine(int row) const;
|
||||||
|
std::string getSeparatorLine() const;
|
||||||
|
char getCellChar(uint8_t value) const;
|
||||||
|
|
||||||
|
// Constants for formatting
|
||||||
|
static constexpr int GRID_WIDTH = 21; // "1 2 3 | 4 5 6 | 7 8 9"
|
||||||
|
static constexpr int GRID_HEIGHT = 11; // 9 rows + 2 separators
|
||||||
|
static constexpr int TOTAL_HEIGHT = 15; // Grid + title + status
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nonogram Console Renderer
|
||||||
|
*
|
||||||
|
* Renders Nonogram puzzles with hints and solution grid.
|
||||||
|
* Supports real-time updates for solving animations.
|
||||||
|
*/
|
||||||
|
class NonogramRenderer : public ConsoleRenderer {
|
||||||
|
public:
|
||||||
|
explicit NonogramRenderer(const class Nonogram& nonogram);
|
||||||
|
|
||||||
|
void allocateSpace() override;
|
||||||
|
void render() override;
|
||||||
|
void clear() override;
|
||||||
|
|
||||||
|
// Nonogram-specific methods
|
||||||
|
void renderWithState(const std::vector<std::vector<int>>& state);
|
||||||
|
void showSolvingProgress(const std::string& status = "");
|
||||||
|
|
||||||
|
private:
|
||||||
|
const class Nonogram& nonogram_;
|
||||||
|
|
||||||
|
// Rendering helpers
|
||||||
|
std::string formatNonogramLine(int row, const std::vector<std::vector<int>>* state = nullptr) const;
|
||||||
|
std::string formatColumnHints() const;
|
||||||
|
int calculateWidth() const;
|
||||||
|
int calculateHeight() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utility class for demo animations
|
||||||
|
class DemoAnimator {
|
||||||
|
public:
|
||||||
|
static void typewriterEffect(const std::string& text, int delay_ms = 50);
|
||||||
|
static void fadeIn(const std::vector<std::string>& lines, int delay_ms = 100);
|
||||||
|
static void progressBar(const std::string& label, int current, int total, int width = 30);
|
||||||
|
};
|
||||||
BIN
demos/sudoku_demo_renderer
Executable file
BIN
demos/sudoku_demo_renderer
Executable file
Binary file not shown.
188
demos/sudoku_demo_renderer.cpp
Normal file
188
demos/sudoku_demo_renderer.cpp
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
#include "console_renderer.h"
|
||||||
|
#include "sudoku/sudoku.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sudoku Demo with Real-time Console Rendering
|
||||||
|
*
|
||||||
|
* This demo shows how to use the SudokuRenderer to display and animate
|
||||||
|
* a Sudoku puzzle being solved in real-time without adding new lines.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void demonstrateSudokuRendering() {
|
||||||
|
std::cout << "=== SUDOKU CONSOLE RENDERING DEMO ===" << std::endl;
|
||||||
|
std::cout << "This demo will show a Sudoku puzzle and simulate solving it." << std::endl;
|
||||||
|
std::cout << "Press Enter to continue..." << std::endl;
|
||||||
|
std::cin.get();
|
||||||
|
|
||||||
|
// Clear screen for clean demo
|
||||||
|
ConsoleRenderer::clearScreen();
|
||||||
|
|
||||||
|
// Create a sample Sudoku puzzle
|
||||||
|
std::string puzzle = "530070000600195000098000060800060003400803001700020006060000280000419005000080079";
|
||||||
|
Sudoku sudoku(puzzle);
|
||||||
|
|
||||||
|
// Create renderer
|
||||||
|
SudokuRenderer renderer(sudoku);
|
||||||
|
|
||||||
|
// Allocate space first (this is crucial!)
|
||||||
|
std::cout << "Allocating console space..." << std::endl;
|
||||||
|
ConsoleRenderer::sleep(1000);
|
||||||
|
renderer.allocateSpace();
|
||||||
|
|
||||||
|
// Initial render
|
||||||
|
std::cout << "Rendering initial puzzle..." << std::endl;
|
||||||
|
ConsoleRenderer::sleep(500);
|
||||||
|
renderer.render();
|
||||||
|
|
||||||
|
// Wait a moment
|
||||||
|
ConsoleRenderer::sleep(2000);
|
||||||
|
|
||||||
|
// Simulate solving some cells with animation
|
||||||
|
std::vector<std::tuple<int, int, uint8_t>> moves = {
|
||||||
|
{0, 2, 1}, {0, 5, 8}, {0, 7, 4}, {0, 8, 2},
|
||||||
|
{1, 0, 2}, {1, 3, 4}, {1, 6, 7}, {1, 8, 3},
|
||||||
|
{2, 2, 7}, {2, 4, 3}, {2, 5, 2}, {2, 7, 6},
|
||||||
|
{3, 1, 9}, {3, 3, 5}, {3, 6, 2}, {3, 8, 7},
|
||||||
|
{4, 0, 5}, {4, 2, 6}, {4, 6, 9}, {4, 8, 8}
|
||||||
|
};
|
||||||
|
|
||||||
|
renderer.showSolvingProgress("Starting to solve...");
|
||||||
|
ConsoleRenderer::sleep(1000);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < moves.size(); ++i) {
|
||||||
|
auto [row, col, value] = moves[i];
|
||||||
|
|
||||||
|
// Update status
|
||||||
|
std::string status = "Solving step " + std::to_string(i + 1) + " of " + std::to_string(moves.size());
|
||||||
|
renderer.showSolvingProgress(status);
|
||||||
|
|
||||||
|
// Set the value
|
||||||
|
sudoku.set(row, col, value);
|
||||||
|
|
||||||
|
// Animate the change
|
||||||
|
renderer.animateCell(row, col, value, 200);
|
||||||
|
|
||||||
|
// Small delay between moves
|
||||||
|
ConsoleRenderer::sleep(300);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final render
|
||||||
|
renderer.showSolvingProgress("Partial solution completed!");
|
||||||
|
ConsoleRenderer::sleep(2000);
|
||||||
|
|
||||||
|
// Show final state
|
||||||
|
if (sudoku.isSolved()) {
|
||||||
|
renderer.showSolvingProgress("PUZZLE SOLVED!");
|
||||||
|
} else {
|
||||||
|
renderer.showSolvingProgress("Partial solution (demo complete)");
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleRenderer::sleep(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void demonstrateRealTimeUpdates() {
|
||||||
|
std::cout << "\n=== REAL-TIME UPDATE DEMO ===" << std::endl;
|
||||||
|
std::cout << "This shows how to update the puzzle without adding lines." << std::endl;
|
||||||
|
std::cout << "Press Enter to continue..." << std::endl;
|
||||||
|
std::cin.get();
|
||||||
|
|
||||||
|
ConsoleRenderer::clearScreen();
|
||||||
|
|
||||||
|
// Create empty sudoku
|
||||||
|
Sudoku sudoku;
|
||||||
|
SudokuRenderer renderer(sudoku);
|
||||||
|
|
||||||
|
// Allocate and render
|
||||||
|
renderer.allocateSpace();
|
||||||
|
renderer.render();
|
||||||
|
|
||||||
|
ConsoleRenderer::sleep(1000);
|
||||||
|
|
||||||
|
// Add numbers one by one
|
||||||
|
std::vector<std::tuple<int, int, uint8_t>> sequence = {
|
||||||
|
{0, 0, 5}, {0, 1, 3}, {0, 4, 7},
|
||||||
|
{1, 0, 6}, {1, 3, 1}, {1, 4, 9}, {1, 5, 5},
|
||||||
|
{2, 1, 9}, {2, 2, 8}, {2, 7, 6},
|
||||||
|
{3, 0, 8}, {3, 4, 6}, {3, 8, 3},
|
||||||
|
{4, 0, 4}, {4, 3, 8}, {4, 5, 3}, {4, 8, 1}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& [row, col, value] : sequence) {
|
||||||
|
renderer.showSolvingProgress("Adding number " + std::to_string(value) +
|
||||||
|
" at (" + std::to_string(row + 1) + "," + std::to_string(col + 1) + ")");
|
||||||
|
|
||||||
|
sudoku.set(row, col, value);
|
||||||
|
renderer.renderWithHighlight(row, col);
|
||||||
|
ConsoleRenderer::sleep(800);
|
||||||
|
|
||||||
|
renderer.render();
|
||||||
|
ConsoleRenderer::sleep(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.showSolvingProgress("Real-time update demo complete!");
|
||||||
|
ConsoleRenderer::sleep(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showAPIUsage() {
|
||||||
|
std::cout << "\n=== API USAGE EXAMPLE ===" << std::endl;
|
||||||
|
std::cout << "Here's how to use the SudokuRenderer API:" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "// 1. Create a Sudoku puzzle" << std::endl;
|
||||||
|
std::cout << "Sudoku sudoku(\"530070000600195000...\");" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "// 2. Create renderer" << std::endl;
|
||||||
|
std::cout << "SudokuRenderer renderer(sudoku);" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "// 3. IMPORTANT: Allocate space first!" << std::endl;
|
||||||
|
std::cout << "renderer.allocateSpace();" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "// 4. Render initial state" << std::endl;
|
||||||
|
std::cout << "renderer.render();" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "// 5. Update puzzle and re-render" << std::endl;
|
||||||
|
std::cout << "sudoku.set(0, 0, 5);" << std::endl;
|
||||||
|
std::cout << "renderer.render(); // Updates in place!" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "// 6. Optional: Use animations" << std::endl;
|
||||||
|
std::cout << "renderer.animateCell(row, col, value);" << std::endl;
|
||||||
|
std::cout << "renderer.showSolvingProgress(\"Status\");" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Press Enter to continue..." << std::endl;
|
||||||
|
std::cin.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
try {
|
||||||
|
// Hide cursor for cleaner display
|
||||||
|
ConsoleRenderer::hideCursor();
|
||||||
|
|
||||||
|
// Run demonstrations
|
||||||
|
showAPIUsage();
|
||||||
|
demonstrateSudokuRendering();
|
||||||
|
demonstrateRealTimeUpdates();
|
||||||
|
|
||||||
|
// Show cursor again
|
||||||
|
ConsoleRenderer::showCursor();
|
||||||
|
|
||||||
|
std::cout << "\n=== DEMO COMPLETE ===" << std::endl;
|
||||||
|
std::cout << "The SudokuRenderer provides a clean API for real-time puzzle rendering!" << std::endl;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
ConsoleRenderer::showCursor();
|
||||||
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1
prompts/6-console-renderer
Normal file
1
prompts/6-console-renderer
Normal file
@@ -0,0 +1 @@
|
|||||||
|
I want you to create code files in the demos/ directory that helps with console rendering. I want to be able to render the sudoku and nonogram demos that I have right now, but also have them solved in real time without adding new lines to the console. Test out your solution by rendering a sudoku puzzle. Give a clear API to render the state of the existing Sudoku class. Make sure you first allocate enough space in the console before rendering anything. So first add some empty lines before the rendering.
|
||||||
@@ -3,17 +3,12 @@ include(FetchContent)
|
|||||||
# Fetch Google Test
|
# Fetch Google Test
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
googletest
|
googletest
|
||||||
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
|
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
|
||||||
URL_HASH SHA256=8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7
|
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(googletest)
|
FetchContent_MakeAvailable(googletest)
|
||||||
|
|
||||||
set(TEST_SOURCES
|
set(TEST_SOURCES
|
||||||
test_main.cpp
|
test_main.cpp
|
||||||
test_wfc.cpp
|
|
||||||
test_grid.cpp
|
|
||||||
test_wave.cpp
|
|
||||||
test_propagator.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create test executable
|
# Create test executable
|
||||||
|
|||||||
Reference in New Issue
Block a user