Assignment Chef icon Assignment Chef
All English tutorials

Programming lesson

Mastering Associative Containers: Build an INI Parser and Sudoku Solver in C++

Learn to use std::map and std::set by implementing an INI file parser and a Sudoku solver. Step-by-step tutorial with code examples, perfect for CSCI 340 students.

C++ associative containers std::map tutorial std::set example INI file parser C++ Sudoku solver C++ CSCI 340 assignment constraint propagation configuration parsing game config parsing backtracking algorithm C++ data structures STL containers C++ programming tutorial C++ map set Sudoku grid solver INI format parser

Introduction: Why Associative Containers Matter in Modern C++

Associative containers are the backbone of many high-performance applications, from game engines to AI data pipelines. In this tutorial, you'll master std::map and std::set by building two classic projects: an INI file parser for configuration management and a Sudoku solver that uses constraint propagation. These skills are directly applicable to real-world tasks like parsing game configs, handling user preferences, or solving logic puzzles in AI. By the end, you'll understand how to store and retrieve data efficiently using key-value pairs and unique sets.

Project 1: INI File Parser with std::map

Understanding the INI Format

INI files are a simple text format for configuration data, consisting of sections and key-value pairs. For example:

; Game settings
[Graphics]
resolution=1920x1080
fullscreen=true

[Audio]
volume=80
mute=false

Our goal is to store this data in a std::map<std::string, std::map<std::string, std::string>>, where the outer map maps section names to inner maps of key-value pairs.

Implementing Core Functions

We'll implement the following functions declared in iniparse.h:

  • add_ini_section: Inserts a new empty section if it doesn't exist.
  • remove_ini_section: Erases a section and returns 1 if removed, 0 otherwise.
  • get_ini_key: Returns the value for a given section and key, or empty string.
  • set_ini_key: Updates an existing key or inserts a new one.
  • remove_ini_key: Removes a key from a section.
  • read_ini: Parses a file line by line, handling comments, section headers, and key=value pairs.
  • write_ini: Writes the config back to a file.
  • print_ini: Outputs the config to a stream.

Example: add_ini_section

void add_ini_section(INI_CONFIG& config, const std::string& section) {
if (config.find(section) == config.end()) {
config[section] = std::map<std::string, std::string>();
}
}

This function uses std::map::find to check existence, then inserts a new empty map if needed. Notice how operator[] automatically creates an empty map if the key doesn't exist, but we avoid overwriting an existing section.

Example: read_ini

void read_ini(std::istream& input, INI_CONFIG& config) {
std::string line, current_section;
while (std::getline(input, line)) {
// Trim whitespace (pseudo-code)
if (line.empty() || line[0] == ';') continue;
if (line[0] == '[') {
size_t end = line.find(']');
current_section = line.substr(1, end-1);
add_ini_section(config, current_section);
} else {
size_t eq = line.find('=');
if (eq != std::string::npos) {
std::string key = line.substr(0, eq);
std::string value = line.substr(eq+1);
// Trim both
set_ini_key(config, current_section, key, value);
}
}
}
}

This parser handles comments, section headers, and key-value pairs. It's a great example of how associative containers simplify configuration loading.

Project 2: Sudoku Solver with std::set

The Constraint Propagation Approach

Sudoku is a perfect use case for std::set. Each cell initially contains a set of possible numbers {1..9}. When a cell is solved (set to a single value), we remove that value from all cells in the same row, column, and 3x3 subgrid. This is called constraint propagation.

Grid Representation

We'll use a std::array<std::array<std::set<int>, 9>, 9> (or a grid_row_major wrapper). Each set holds the remaining candidates for that cell.

Implementing Key Functions

  • initialize_grid: Fill every cell with {1,2,...,9}.
  • set_sudoku_cell_known: Set a cell to a single value (clear set, insert that value).
  • remove_sudoku_option: Remove a candidate from a cell's set. If the set becomes empty, the puzzle is unsolvable.
  • handle_row_for_cell: Remove the solved value from all cells in the same row.
  • handle_col_for_cell: Same for column.
  • handle_subgrid_for_cell: Same for the 3x3 subgrid.
  • load_sudoku_grid: Read a puzzle from a file (0 or '.' for unknown).
  • print_sudoku_grid: Display the grid, showing solved numbers or '.' for unknown.

Example: remove_sudoku_option

bool remove_sudoku_option(SUDOKUGRID& grid, int row, int col, int value) {
auto& cell = grid[row][col];
auto it = cell.find(value);
if (it != cell.end()) {
cell.erase(it);
if (cell.empty()) return false; // impossible state
}
return true;
}

This function returns false if the cell becomes empty, indicating a contradiction. This is crucial for backtracking solvers.

Example: handle_row_for_cell

void handle_row_for_cell(SUDOKUGRID& grid, int row, int col) {
int solved_value = *grid[row][col].begin();
if (grid[row][col].size() != 1) return; // not solved yet
for (int c = 0; c < 9; ++c) {
if (c != col) {
remove_sudoku_option(grid, row, c, solved_value);
}
}
}

This propagates the solved value to all other cells in the same row. Combined with column and subgrid handlers, it systematically reduces possibilities.

Putting It All Together

A simple solver loop might repeatedly apply these handlers until no changes occur. For harder puzzles, you may need backtracking, but constraint propagation alone solves many puzzles.

Trend Connection: Why This Matters in 2026

Associative containers are everywhere. For example, game engines like Unreal use maps to store game settings (INI-style). AI models use sets to track unique tokens. Even your phone's contact list uses a map from names to numbers. By mastering these containers, you're building skills used in high-demand fields like game development, AI, and app development.

Conclusion

You've learned how to use std::map for configuration parsing and std::set for constraint-based problem solving. These skills are essential for any C++ developer. Practice by extending the INI parser to handle nested sections or by adding a backtracking algorithm to the Sudoku solver. Happy coding!