First pass at a sudoku solver: Puzzle Import
Imports a puzzle from a string and displays the restricted puzzle.
This commit is contained in:
commit
bcc5b5097e
|
@ -0,0 +1 @@
|
||||||
|
BasedOnStyle: Google
|
|
@ -0,0 +1,2 @@
|
||||||
|
build/
|
||||||
|
builddir/
|
|
@ -0,0 +1,14 @@
|
||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
|
||||||
|
project(Sudoku)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
set(SOURCE_FILES
|
||||||
|
solver/cell.cpp
|
||||||
|
solver/puzzle.cpp
|
||||||
|
solver.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(solver ${SOURCE_FILES})
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "solver/puzzle.h"
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
Puzzle puzzle = Puzzle::FromString(
|
||||||
|
"3..4.162.1...8.4....5.2.83..578........7..5.3..29.4..748.53..1.2.3.9...."
|
||||||
|
".7...6.9.");
|
||||||
|
std::cout << "https://tiramisu.one/sudoku.html?p=" << puzzle.CurrentState()
|
||||||
|
<< "&m=" << puzzle.PencilMarkState() << std::endl;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include "cell.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
Cell::Cell()
|
||||||
|
: state_(Unsolved), value_(0), possibilities_{true, true, true, true, true,
|
||||||
|
true, true, true, true} {}
|
||||||
|
|
||||||
|
Cell::Cell(uint8_t value)
|
||||||
|
: state_(Solved), value_(value), possibilities_{false, false, false,
|
||||||
|
false, false, false,
|
||||||
|
false, false, false} {}
|
||||||
|
|
||||||
|
void Cell::Restrict(uint8_t value) {
|
||||||
|
assert(value >= 1 && value <= 9);
|
||||||
|
possibilities_[value - 1] = false;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
class Cell {
|
||||||
|
public:
|
||||||
|
enum State {
|
||||||
|
Unsolved,
|
||||||
|
Solved,
|
||||||
|
};
|
||||||
|
Cell();
|
||||||
|
explicit Cell(uint8_t value);
|
||||||
|
|
||||||
|
void Restrict(uint8_t value);
|
||||||
|
|
||||||
|
bool IsSolved() const { return state_ == Solved; }
|
||||||
|
uint8_t value() const { return value_; }
|
||||||
|
bool IsPossible(uint8_t v) const { return possibilities_[v - 1]; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
State state_;
|
||||||
|
uint8_t value_;
|
||||||
|
std::array<bool, 9> possibilities_;
|
||||||
|
};
|
|
@ -0,0 +1,88 @@
|
||||||
|
#include "puzzle.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
uint8_t RowStart(uint8_t id) { return (id / 9) * 9; }
|
||||||
|
uint8_t ColStart(uint8_t id) { return id % 9; }
|
||||||
|
uint8_t BoxStart(uint8_t id) {
|
||||||
|
uint8_t row = (RowStart(id) / 27) * 27;
|
||||||
|
uint8_t col = (ColStart(id) / 3) * 3;
|
||||||
|
return row + col;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::array<uint8_t, 9> kBoxOffsets{0, 1, 2, 9, 10, 11, 18, 19, 20};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Puzzle Puzzle::FromString(std::string puzzle) {
|
||||||
|
assert(puzzle.length() == 81);
|
||||||
|
Puzzle p;
|
||||||
|
|
||||||
|
for (int i = 0; i < 81; i++) {
|
||||||
|
if (puzzle[i] == '.' || puzzle[i] == '0') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int diff = puzzle[i] - '0';
|
||||||
|
if (diff < 1 || diff > 9) {
|
||||||
|
assert(false && "Invalid input character");
|
||||||
|
}
|
||||||
|
p.AssignSquare(i, diff);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Puzzle::CurrentState() {
|
||||||
|
std::ostringstream str;
|
||||||
|
for (const Cell& c : cells_) {
|
||||||
|
if (c.IsSolved()) {
|
||||||
|
str << (int)c.value();
|
||||||
|
} else {
|
||||||
|
str << '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Puzzle::PencilMarkState() {
|
||||||
|
std::ostringstream str;
|
||||||
|
for (const Cell& c : cells_) {
|
||||||
|
for (uint8_t i = 1; i <= 9; i++) {
|
||||||
|
if (c.IsPossible(i)) {
|
||||||
|
str << (int)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str << ",";
|
||||||
|
}
|
||||||
|
// Erase the trailing ",".
|
||||||
|
std::string temp = str.str();
|
||||||
|
temp.erase(temp.end() - 1);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Puzzle::AssignSquare(uint8_t id, uint8_t value) {
|
||||||
|
assert(id < 81);
|
||||||
|
assert(value >= 0 && value <= 9);
|
||||||
|
|
||||||
|
cells_[id] = Cell(value);
|
||||||
|
|
||||||
|
const uint8_t row = RowStart(id);
|
||||||
|
for (uint8_t i = row; i < row + 9; i++) {
|
||||||
|
cells_[i].Restrict(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t col = ColStart(id);
|
||||||
|
for (uint8_t i = col; i < 81; i += 9) {
|
||||||
|
cells_[i].Restrict(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t box = BoxStart(id);
|
||||||
|
for (uint8_t offset : kBoxOffsets) {
|
||||||
|
cells_[box + offset].Restrict(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Puzzle::Puzzle() {}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "cell.h"
|
||||||
|
|
||||||
|
class Puzzle {
|
||||||
|
public:
|
||||||
|
static Puzzle FromString(std::string puzzle);
|
||||||
|
|
||||||
|
std::string CurrentState();
|
||||||
|
std::string PencilMarkState();
|
||||||
|
|
||||||
|
void AssignSquare(uint8_t id, uint8_t value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Puzzle();
|
||||||
|
std::array<Cell, 81> cells_;
|
||||||
|
};
|
Loading…
Reference in New Issue