49fa333c-bfa3-47e6-9332-0826c9982565
This commit is contained in:
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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user