build sudoku + sudoku solver from scratch
Bài 1 — Phase 1 của app sudoku. Định nghĩa `Grid` = `std::array<std::array<int, 9>, 9>` + `FixedMap`. Dựng class `App` RAII bao bọc GLFW + context ImGui. Render bảng ImGui 9x9 với checkerboard 3x3, ô fixed chữ trắng, ô editable dùng `InputText` với `PushID/PopID` để widget ID duy nhất.
X\u00e2y d\u1ef1ng Sudoku Solver t\u1eeb \u0111\u1ea7u, B\u00e0i 1: Grid v\u00e0 UI c\u01a1 b\u1ea3n v\u1edbi ImGui
M\u1ee5c ti\u00eau c\u1ee7a series n\u00e0y l\u00e0 vi\u1ebft m\u1ed9t \u1ee9ng d\u1ee5ng Sudoku Solver ho\u00e0n ch\u1ec9nh b\u1eb1ng C++, t\u1eeb render UI cho \u0111\u1ebfn thu\u1eadt to\u00e1n gi\u1ea3i. Trong Phase 1, ta d\u1eebng l\u1ea1i \u1edf m\u1ed9t th\u1ee9 t\u01b0\u1edfng nh\u1ecf nh\u01b0ng quy\u1ebft \u0111\u1ecbnh to\u00e0n b\u1ed9 ki\u1ebfn tr\u00fac v\u1ec1 sau: \u0111\u1ecbnh ngh\u0129a ki\u1ec3u Grid, d\u1ef1ng class App theo RAII, v\u00e0 render \u0111\u01b0\u1ee3c b\u1ea3ng 9x9 v\u1edbi ImGui sao cho \u00f4 fixed (\u00f4 \u0111\u1ec1 b\u00e0i) ph\u00e2n bi\u1ec7t r\u00f5 v\u1edbi \u00f4 editable (\u00f4 ng\u01b0\u1eddi ch\u01a1i nh\u1eadp).
Phase 1 kh\u00f4ng c\u00f3 solver. Kh\u00f4ng c\u00f3 check valid. Kh\u00f4ng c\u00f3 undo/redo. Ch\u1ec9 c\u00f3 m\u1ed9t c\u1eeda s\u1ed5 GLFW + ImGui m\u1edf l\u00ean, hi\u1ec7n 81 \u00f4 vu\u00f4ng, m\u1ed7i \u00f4 l\u00e0 m\u1ed9t widget \u0111\u1ed9c l\u1eadp, v\u00e0 b\u1ea1n c\u00f3 th\u1ec3 click v\u00e0o \u00f4 editable \u0111\u1ec3 g\u00f5 s\u1ed1. Nh\u01b0ng \u0111\u1ec3 \u0111\u1ebfn \u0111\u01b0\u1ee3c \u0111\u00f3, c\u00f3 4 th\u1ee9 c\u1ea7n \u0111\u1eb7t \u0111\u00fang t\u1eeb \u0111\u1ea7u. Sai m\u1ed9t b\u01b0\u1edbc s\u1ebd ph\u1ea3i vi\u1ebft l\u1ea1i to\u00e0n b\u1ed9 \u1edf Phase 2.
\u0110\u1ecbnh ngh\u0129a Grid, v\u00ec sao ch\u1ecdn std::array thay v\u00ec std::vector
Quy\u1ebft \u0111\u1ecbnh nh\u1ecf nh\u1ea5t nh\u01b0ng k\u00e9o d\u00e0i c\u1ea3 series:
#include <array>
#include <bitset>
using Grid = std::array<std::array<int, 9>, 9>;
using FixedMap = std::bitset<81>;
Grid l\u00e0 m\u1ed9t m\u1ea3ng 2 chi\u1ec1u 9x9 ch\u1ee9a s\u1ed1 nguy\u00ean (0 ngh\u0129a l\u00e0 \u00f4 tr\u1ed1ng, 1-9 l\u00e0 gi\u00e1 tr\u1ecb). FixedMap l\u00e0 m\u1ed9t bitset 81 bit, \u0111\u00e1nh d\u1ea5u \u00f4 n\u00e0o thu\u1ed9c \u0111\u1ec1 b\u00e0i (fixed) v\u00e0 \u00f4 n\u00e0o ng\u01b0\u1eddi ch\u01a1i nh\u1eadp \u0111\u01b0\u1ee3c (editable).
V\u00ec sao std::array ch\u1ee9 kh\u00f4ng ph\u1ea3i std::vector<std::vector<int>>?
- K\u00edch th\u01b0\u1edbc c\u1ed1 \u0111\u1ecbnh \u1edf compile time. B\u1ea3ng Sudoku lu\u00f4n l\u00e0 9x9. Kh\u00f4ng c\u00f3 l\u00fd do tr\u1ea3 ti\u1ec1n cho allocation \u0111\u1ed9ng.
- Layout li\u1ec1n nhau trong b\u1ed9 nh\u1edb.
std::array<std::array<int, 9>, 9>th\u1ef1c ch\u1ea5t l\u00e0 81 int c\u1ea1nh nhau trong stack ho\u1eb7c trong m\u1ed9t struct, cache-friendly.vector<vector<int>>l\u1ea1i l\u00e0 m\u1ed9t m\u1ea3ng c\u00e1c con tr\u1ecf t\u1edbi 9 vector ri\u00eang bi\u1ec7t. M\u1ed7i l\u1ea7n \u0111\u1ecdc 1 h\u00e0ng, CPU ph\u1ea3i \u0111i qua m\u1ed9t l\u1edbp indirection. - Copy r\u1ebb v\u00e0 r\u00f5 r\u00e0ng. Sao ch\u00e9p
Gridl\u00e0 sao ch\u00e9p 81 int, tr\u00ean h\u1ea7u h\u1ebft ki\u1ebfn tr\u00fac d\u01b0\u1edbi 1 microsecond. Sau n\u00e0y \u1edf Phase solver, b\u1ea1n s\u1ebd copyGridr\u1ea5t nhi\u1ec1u l\u1ea7n khi backtrack, ch\u1ecdnstd::arrayngay t\u1eeb \u0111\u1ea7u gi\u00fap kh\u1ecfi ph\u1ea3i refactor.
V\u00ec sao std::bitset<81> cho FixedMap ch\u1ee9 kh\u00f4ng ph\u1ea3i std::array<bool, 81> ho\u1eb7c std::vector<bool>?
std::bitset<81> chi\u1ebfm ch\u00ednh x\u00e1c 16 byte (2 word 64-bit, ph\u1ea7n cu\u1ed1i c\u00f2n d\u01b0 47 bit). std::array<bool, 81> chi\u1ebfm 81 byte, m\u1ed7i bool 1 byte. Ti\u1ebft ki\u1ec7m b\u1ed9 nh\u1edb l\u00e0 chuy\u1ec7n nh\u1ecf \u1edf quy m\u00f4 Sudoku, nh\u01b0ng API c\u1ee7a bitset r\u00f5 r\u00e0ng h\u01a1n: .test(idx), .set(idx), .reset(idx). B\u1ea1n di\u1ec5n \u0111\u1ea1t "\u00f4 n\u00e0y c\u00f3 fixed kh\u00f4ng" b\u1eb1ng m\u1ed9t c\u00e2u duy nh\u1ea5t, kh\u00f4ng ph\u1ea3i fixed_arr[row * 9 + col] m\u1ed7i l\u1ea7n.
C\u00e1ch map t\u1eeb (row, col) sang index trong bitset:
constexpr int idx(int row, int col) {
return row * 9 + col;
}
\u0110\u1eb7t constexpr \u0111\u1ec3 compiler c\u00f3 th\u1ec3 t\u00ednh s\u1eb5n n\u1ebfu row v\u00e0 col l\u00e0 literal. B\u1ea1n s\u1ebd th\u1ea5y l\u1ee3i \u00edch khi vi\u1ebft test case v\u1edbi index hard-coded.
Class App, RAII bao b\u1ecdc GLFW v\u00e0 ImGui
ImGui l\u00e0 m\u1ed9t th\u01b0 vi\u1ec7n "immediate mode". B\u1ea1n kh\u00f4ng "kh\u1edfi t\u1ea1o widget r\u1ed3i gi\u1eef con tr\u1ecf t\u1edbi n\u00f3" nh\u01b0 Qt hay GTK. M\u1ed7i frame, b\u1ea1n g\u1ecdi l\u1ea1i ImGui::InputText(...), ImGui::Button(...), v\u00e0 ImGui t\u1ef1 lo state th\u00f4ng qua m\u1ed9t context object global.
V\u1ea5n \u0111\u1ec1 \u1edf \u0111\u00e2y: context object c\u1ee7a ImGui kh\u00f4ng t\u1ef1 d\u1ecdn d\u1eb9p. N\u1ebfu b\u1ea1n qu\u00ean g\u1ecdi ImGui::DestroyContext(), app s\u1ebd leak. N\u1ebfu qu\u00ean g\u1ecdi glfwTerminate(), c\u00e1c t\u00e0i nguy\u00ean OS kh\u00f4ng tr\u1ea3 l\u1ea1i. C\u00e1ch an to\u00e0n l\u00e0 g\u00f3i v\u00e0o RAII:
class App {
public:
App() {
if (!glfwInit()) {
throw std::runtime_error("glfwInit failed");
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
window_ = glfwCreateWindow(540, 600, "Sudoku Solver", nullptr, nullptr);
if (!window_) {
glfwTerminate();
throw std::runtime_error("glfwCreateWindow failed");
}
glfwMakeContextCurrent(window_);
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui_ImplGlfw_InitForOpenGL(window_, true);
ImGui_ImplOpenGL3_Init("#version 330");
}
~App() {
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
if (window_) {
glfwDestroyWindow(window_);
}
glfwTerminate();
}
App(const App&) = delete;
App& operator=(const App&) = delete;
void run();
private:
GLFWwindow* window_ = nullptr;
Grid grid_{};
FixedMap fixed_{};
void render_board();
};
V\u00e0i chi ti\u1ebft \u0111\u00e1ng ch\u00fa \u00fd:
- Constructor c\u00f3 th\u1ec3 throw. C\u00e1ch r\u00f5 r\u00e0ng nh\u1ea5t \u0111\u1ec3 b\u00e1o l\u1ed7i init l\u00e0
throw std::runtime_error. Destructor s\u1ebd KH\u00d4NG \u0111\u01b0\u1ee3c g\u1ecdi n\u1ebfu constructor throw, n\u00ean tr\u01b0\u1edbc khi throw l\u1ea7n th\u1ee9 hai, taglfwTerminate()th\u1ee7 c\u00f4ng. - Copy b\u1ecb c\u1ea5m. M\u1ed9t context GLFW + ImGui kh\u00f4ng th\u1ec3 c\u00f3 hai ch\u1ee7 s\u1edf h\u1eefu. \u0110\u00e1nh d\u1ea5u
= delete\u0111\u1ec3 compiler b\u00e1o l\u1ed7i t\u1ea1i ch\u1ed7 n\u1ebfu ai \u0111\u00f3 v\u00f4 t\u00ecnh vi\u1ebftApp a = b;. grid_v\u00e0fixed_l\u00e0 member. Kh\u1edfi t\u1ea1o b\u1eb1ng{}(zero-initialization). T\u1ea5t c\u1ea3 \u00f4 b\u1eb1ng 0, kh\u00f4ng \u00f4 n\u00e0o fixed. \u0110\u00e2y l\u00e0 tr\u1ea1ng th\u00e1i "b\u1ea3ng tr\u1ed1ng" m\u1eb7c \u0111\u1ecbnh.
T\u00e1ch render_board() th\u00e0nh private method gi\u00fap run() ch\u1ec9 l\u00e0 v\u00f2ng l\u1eb7p khung s\u01b0\u1eddn:
void App::run() {
while (!glfwWindowShouldClose(window_)) {
glfwPollEvents();
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
render_board();
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(window_, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window_);
}
}
V\u00f2ng l\u1eb7p n\u00e0y l\u00e0 khung chu\u1ea9n c\u1ee7a m\u1ecdi \u1ee9ng d\u1ee5ng ImGui: NewFrame \u1edf \u0111\u1ea7u, Render \u1edf gi\u1eefa, SwapBuffers \u1edf cu\u1ed1i. To\u00e0n b\u1ed9 logic v\u1ebd Sudoku n\u1eb1m trong render_board().
Render b\u1ea3ng 9x9, checkerboard 3x3 v\u00e0 InputText cho \u00f4 editable
\u0110\u00e2y l\u00e0 ph\u1ea7n kh\u00f3 nh\u1ea5t c\u1ee7a Phase 1. C\u00f3 3 y\u00eau c\u1ea7u ch\u1ed3ng ch\u00e9o:
- M\u1ed7i \u00f4 l\u00e0 m\u1ed9t widget ri\u00eang. Khi click v\u00e0o \u00f4 (3, 5), kh\u00f4ng \u0111\u01b0\u1ee3c focus nh\u1ea7m v\u00e0o \u00f4 (3, 6).
- \u00d4 fixed (\u0111\u1ec1 b\u00e0i) hi\u1ec3n th\u1ecb text tr\u1eafng, kh\u00f4ng cho g\u00f5. \u00d4 editable cho g\u00f5 s\u1ed1 1-9.
- Background c\u1ee7a 9 block 3x3 ph\u1ea3i xen k\u1ebd checkerboard. Block (0,0), (0,2), (1,1), (2,0), (2,2) m\u1ed9t m\u00e0u, c\u00e1c block c\u00f2n l\u1ea1i m\u00e0u kh\u00e1c, gi\u00fap ng\u01b0\u1eddi ch\u01a1i nh\u00ecn r\u00f5 ranh gi\u1edbi block.
Tri\u1ec3n khai:
void App::render_board() {
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(540, 600));
ImGui::Begin("##board", nullptr,
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove);
const float cell_size = 56.0f;
const ImVec4 light_block = ImVec4(0.20f, 0.20f, 0.22f, 1.0f);
const ImVec4 dark_block = ImVec4(0.12f, 0.12f, 0.14f, 1.0f);
for (int row = 0; row < 9; ++row) {
for (int col = 0; col < 9; ++col) {
if (col > 0) ImGui::SameLine();
const int block_row = row / 3;
const int block_col = col / 3;
const bool is_light = (block_row + block_col) % 2 == 0;
const ImVec4 bg = is_light ? light_block : dark_block;
ImGui::PushID(idx(row, col));
if (fixed_.test(idx(row, col))) {
ImGui::PushStyleColor(ImGuiCol_Button, bg);
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 1, 1, 1));
char label[3];
std::snprintf(label, sizeof(label), "%d", grid_[row][col]);
ImGui::Button(label, ImVec2(cell_size, cell_size));
ImGui::PopStyleColor(2);
} else {
ImGui::PushStyleColor(ImGuiCol_FrameBg, bg);
ImGui::SetNextItemWidth(cell_size);
char buf[3] = {};
if (grid_[row][col] != 0) {
std::snprintf(buf, sizeof(buf), "%d", grid_[row][col]);
}
if (ImGui::InputText("", buf, sizeof(buf),
ImGuiInputTextFlags_CharsDecimal |
ImGuiInputTextFlags_AutoSelectAll)) {
int parsed = std::atoi(buf);
grid_[row][col] = (parsed >= 1 && parsed <= 9) ? parsed : 0;
}
ImGui::PopStyleColor();
}
ImGui::PopID();
}
}
ImGui::End();
}
Ph\u1ea7n \u0111\u00e1ng gi\u1ea3i th\u00edch nh\u1ea5t l\u00e0 PushID(idx(row, col)) v\u00e0 PopID(). ImGui x\u00e1c \u0111\u1ecbnh widget b\u1eb1ng m\u1ed9t stack ID. N\u1ebfu hai widget c\u00f3 c\u00f9ng label "", ImGui s\u1ebd nh\u1ea7m ch\u00fang l\u00e0 m\u1ed9t widget duy nh\u1ea5t, l\u01b0u chung state nh\u1eadp li\u1ec7u, v\u00e0 b\u1ea1n s\u1ebd th\u1ea5y hi\u1ec7n t\u01b0\u1ee3ng "g\u00f5 \u00f4 (0,0) th\u00ec text hi\u1ec7n \u1edf \u00f4 (1,0)". Push th\u00eam m\u1ed9t ID duy nh\u1ea5t cho m\u1ed7i \u00f4 l\u00e0 c\u00e1ch ch\u00ednh th\u1ee9c \u0111\u1ec3 b\u1ea3o widget n\u00e0y \u0111\u1ed9c l\u1eadp.
V\u00ec sao idx(row, col) \u0111\u01b0\u1ee3c truy\u1ec1n v\u00e0o? V\u00ec gi\u00e1 tr\u1ecb 0..80 \u0111\u1ea3m b\u1ea3o duy nh\u1ea5t cho 81 \u00f4. B\u1ea1n c\u00f3 th\u1ec3 truy\u1ec1n row * 9 + col tr\u1ef1c ti\u1ebfp, k\u1ebft qu\u1ea3 nh\u01b0 nhau, nh\u01b0ng g\u1ecdi idx(...) gi\u00fap \u0111\u1ed3ng b\u1ed9 v\u1edbi FixedMap.test(...) \u1edf d\u00f2ng d\u01b0\u1edbi.
PushStyleColor(ImGuiCol_FrameBg, bg) \u0111\u1ed5i background c\u1ee7a widget k\u1ebf ti\u1ebfp. V\u00ec c\u00e1c style \u0111\u01b0\u1ee3c push l\u00ean m\u1ed9t stack, ta ph\u1ea3i PopStyleColor \u0111\u00fang s\u1ed1 l\u1ea7n \u0111\u00e3 push, n\u1ebfu kh\u00f4ng stack s\u1ebd tr\u00e0n sang frame sau v\u00e0 c\u1ea3 app v\u1ebd sai.
T\u1ea1i sao t\u00e1ch fixed_ kh\u1ecfi grid_
M\u1ed9t c\u00e2u h\u1ecfi t\u1ef1 nhi\u00ean: t\u1ea1i sao kh\u00f4ng d\u00f9ng d\u1ea5u hi\u1ec7u grid_[row][col] < 0 \u0111\u1ec3 \u0111\u00e1nh d\u1ea5u \u00f4 fixed (v\u00ed d\u1ee5 -3 ngh\u0129a l\u00e0 \u00f4 fixed c\u00f3 gi\u00e1 tr\u1ecb 3)? Ti\u1ebft ki\u1ec7m \u0111\u01b0\u1ee3c 16 byte b\u1ed9 nh\u1edb.
T\u00e1ch ra c\u00f3 3 l\u1ee3i \u0111i\u1ec3m:
- Logic ng\u1eafn h\u01a1n.
fixed_.test(idx(row, col))\u0111\u1ecdc th\u00e0nh ti\u1ebfng l\u00e0 "\u00f4 n\u00e0y c\u00f3 fixed kh\u00f4ng". Kh\u00f4ng c\u1ea7n gi\u1ea3i th\u00edch quy \u01b0\u1edbc \u00e2m d\u01b0\u01a1ng. - Reset b\u1ea3ng \u0111\u01a1n gi\u1ea3n. Khi ng\u01b0\u1eddi ch\u01a1i reset, b\u1ea1n vi\u1ebft
for (int i = 0; i < 81; ++i) if (!fixed_.test(i)) grid_data[i] = 0;. G\u1ecdn h\u01a1n so v\u1edbi ph\u1ea3i nh\u1edb "\u00f4 fixed mang d\u1ea5u \u00e2m". - Solver d\u00f9ng l\u1ea1i \u0111\u01b0\u1ee3c. \u1ede Phase 3, solver s\u1ebd ghi v\u00e0o
grid_c\u00e1c gi\u00e1 tr\u1ecb th\u1eed. N\u1ebfufixed_l\u00e0 m\u1ed9t bitset ri\u00eang, solver kh\u00f4ng c\u1ea7n ph\u00e2n bi\u1ec7t \u00f4 fixed v\u1edbi \u00f4 \u0111\u00e3 th\u1eed, ch\u1ec9 c\u1ea7nif (!fixed_.test(i)) try_value(i, v);.
So s\u00e1nh: ImGui v\u1edbi SDL2 + OpenGL tr\u1ef1c ti\u1ebfp
V\u00ec sao ch\u1ecdn ImGui ch\u1ee9 kh\u00f4ng ph\u1ea3i SDL2 thu\u1ea7n ho\u1eb7c render v\u1edbi OpenGL tr\u1ef1c ti\u1ebfp?
ImGui chi\u1ebfm kho\u1ea3ng 80% c\u00f4ng vi\u1ec7c setup UI. N\u00f3 t\u1ef1 lo input handling, focus management, text rendering. Chi ph\u00ed: b\u1ea1n kh\u00f4ng ki\u1ec3m so\u00e1t t\u1eebng pixel, layout c\u00f3 gi\u1edbi h\u1ea1n (SameLine, Indent, Columns), kh\u00f4ng th\u00e2n thi\u1ec7n v\u1edbi animation ph\u1ee9c t\u1ea1p.
SDL2 thu\u1ea7n cho b\u1ea1n ki\u1ec3m so\u00e1t ho\u00e0n to\u00e0n nh\u01b0ng ph\u1ea3i t\u1ef1 code rectangle, hit-testing cho click, render font (qua SDL_ttf), v\u00e0 qu\u1ea3n l\u00fd state widget th\u1ee7 c\u00f4ng. Cho Sudoku, m\u1ed9t grid 9x9 v\u1edbi 81 widget \u0111\u1ed9c l\u1eadp, \u00edt state, ImGui ti\u1ebft ki\u1ec7m kho\u1ea3ng 400-600 d\u00f2ng code so v\u1edbi SDL2 thu\u1ea7n. \u0110\u00e2y l\u00e0 m\u1ed9t trade-off r\u00f5 r\u00e0ng: ImGui d\u00e0nh cho c\u00f4ng c\u1ee5 developer v\u00e0 prototype, kh\u00f4ng ph\u1ea3i game AAA.
Build instructions ng\u1eafn
CMake snippet t\u1ed1i thi\u1ec3u (d\u00f9ng FetchContent \u0111\u1ec3 pull ImGui v\u00e0 GLFW):
cmake_minimum_required(VERSION 3.20)
project(sudoku_solver CXX)
set(CMAKE_CXX_STANDARD 20)
include(FetchContent)
FetchContent_Declare(glfw
GIT_REPOSITORY https://github.com/glfw/glfw.git
GIT_TAG 3.4)
FetchContent_MakeAvailable(glfw)
FetchContent_Declare(imgui
GIT_REPOSITORY https://github.com/ocornut/imgui.git
GIT_TAG v1.91.0)
FetchContent_MakeAvailable(imgui)
# add imgui sources, glfw backend, opengl3 backend
# (xem README c\u1ee7a imgui ph\u1ea7n "Using Dear ImGui with cmake")
add_executable(sudoku src/main.cpp src/app.cpp)
target_link_libraries(sudoku PRIVATE glfw imgui)
Tr\u00ean macOS, b\u1ea1n c\u1ea7n th\u00eam target_link_libraries(sudoku PRIVATE "-framework OpenGL"). Tr\u00ean Linux, target_link_libraries(sudoku PRIVATE GL).
Checklist ho\u00e0n th\u00e0nh Phase 1
Tr\u01b0\u1edbc khi sang Phase 2 (load \u0111\u1ec1 b\u00e0i v\u00e0 check valid), ki\u1ec3m tra:
- C\u1eeda s\u1ed5 GLFW m\u1edf l\u00ean, hi\u1ec7n 81 \u00f4 vu\u00f4ng 9x9.
- Block 3x3 c\u00f3 2 m\u00e0u xen k\u1ebd r\u00f5 r\u00e0ng.
- Hard-code th\u1eed
grid_[0][0] = 5; fixed_.set(idx(0, 0));. \u00d4 (0,0) hi\u1ec7n ch\u1eef "5" tr\u1eafng, kh\u00f4ng click \u0111\u01b0\u1ee3c. - Click v\u00e0o \u00f4 kh\u00e1c, g\u00f5 s\u1ed1 1-9, gi\u00e1 tr\u1ecb l\u01b0u v\u00e0o
grid_. - G\u00f5 s\u1ed1 0 ho\u1eb7c k\u00fd t\u1ef1 kh\u00f4ng h\u1ee3p l\u1ec7 th\u00ec kh\u00f4ng crash.
- \u0110\u00f3ng c\u1eeda s\u1ed5 kh\u00f4ng crash, kh\u00f4ng leak (ch\u1ea1y v\u1edbi
valgrind ./sudokun\u1ebfu tr\u00ean Linux).
Phase 2 s\u1ebd hardcode 5 puzzle m\u1eabu, load v\u00e0o grid_ v\u00e0 fixed_, v\u00e0 b\u1eadt check valid theo t\u1eebng d\u00f2ng, c\u1ed9t, block 3x3. \u0110\u00e2y l\u00e0 n\u1ec1n t\u1ea3ng cho solver \u1edf Phase 3.
References
- Dear ImGui repository v\u00e0 wiki: https://github.com/ocornut/imgui
- GLFW official documentation: https://www.glfw.org/documentation.html
std::arrayreference tr\u00ean cppreference: https://en.cppreference.com/w/cpp/container/arraystd::bitsetreference: https://en.cppreference.com/w/cpp/utility/bitset