#include <bits/stdc++.h>
#include <stdexcept>
using namespace std;
class OrientedShape{
public:
// Oriented Shape represents a shape that has been transformed which may exist in 3-dimensions
int rows, columns, height;
vector<vector<vector<bool>>> a;
OrientedShape(int height, int rows, int columns)
: rows(rows), columns(columns), height(height) {
a.resize(height, vector<vector<bool>>(rows, vector<bool>(columns)));
}
void add(int h, int r, int c){
a[h][r][c]=1;
}
// Utility for printing the shape
void print(){
for(int i=0;i<height;i++){
cout<<"Plane "<<i<<":\n";
for(int j=0;j<rows;j++){
for(int k=0;k<columns;k++){
cout<<a[i][j][k]<<" ";
}
cout<<"\n";
}
cout<<"\n\n";
}
}
};
class Shape{
public:
int rows, columns, height=0;
string color;
int id;
vector<vector<bool>> block;
// Construct a shape by specifying the rows, columns and adding the blocks incrementally
Shape(int id, int rows, int columns)
: id(id), rows(rows), columns(columns) {
block.resize(rows, vector<bool>(columns));
}
bool operator==(const Shape& other) const {
return id == other.id;
}
void add(int row, int col){
block[row][col]=1;
}
vector<OrientedShape> getAllOrientations(){
// TODO: to be implemented
OrientedShape oshape(1, rows, columns);
for(int i=0;i<rows;i++){
for(int j=0;j<columns;j++){
oshape.add(0, i, j);
}
}
return {oshape};
}
};
struct ShapeHash {
size_t operator()(const Shape& shape) const {
return hash<int>()(shape.id);
}
};
class Board{
public:
// Assuming the board is a square pyramid with a given height
int height;
vector<vector<vector<int>>> a;
Board(){}
Board(int height) : height(height) {
a.resize(height);
for (int i = 0; i < height; i++) {
a[i].resize(height - i, vector<int>(height - i, -1));
}
}
// This checks if a given coordinates are valid on the board
bool exists(int h, int r, int c){
return h<height and r < (height-h) and c < (height - h);
}
// Add a given shape id to the given coordinates on the board
void add(int height, int row, int col, int id){
if(not exists(height, row, col)){
cout << "Invalid Arguments: Failed to add Shape with "<<id<<" at ("<< height << ", "<< row <<", "<< col <<").\n";
return;
}
if(a[height][row][col]!=-1){
cout<<"Alert: Overriding the location ("<< height << ", "<< row <<", " << col <<").\n";
}
a[height][row][col] = id;
}
// Checks if a given shape can be superimposed at the given coordinates on the board
bool canFit(int h, int r, int c, OrientedShape& oshape){
for(int ch=0;ch<oshape.height;ch++){
for(int cr=0;cr<oshape.rows;cr++){
for(int cc=0;cc<oshape.columns;cc++){
// If there does not exist a ball at the current loc, continue
if(not oshape.a[ch][cr][cc]) continue;
// Check if the coordinate exists and it is empty
if(not exists(h+ch, r+cr, c+cc)) {
return false;
}
if(a[h+ch][r+cr][c+cc]!=-1) {
return false;
}
}
}
}
return true;
}
// Fits a given shape at a specified location on the board
void fit(int h, int r, int c, int shapeId, OrientedShape& oshape){
for(int ch=0;ch<oshape.height;ch++){
for(int cr=0;cr<oshape.rows;cr++){
for(int cc=0;cc<oshape.columns;cc++){
a[h+ch][r+cr][c+cc]=shapeId;
}
}
}
}
// Removes a given shape from the board
void remove(int h, int r, int c, OrientedShape& oshape){
for(int ch=0;ch<oshape.height;ch++){
for(int cr=0;cr<oshape.rows;cr++){
for(int cc=0;cc<oshape.columns;cc++){
a[h+ch][r+cr][c+cc]=-1;
}
}
}
}
// Utility for printing the board
void print(){
for(int i=0;i<height;i++){
cout<<"Plane "<<i+1<<":\n";
for(int j=0;j<height-i;j++){
for(int k=0;k<height-i;k++){
cout<<a[i][j][k]<<" ";
}
cout<<"\n";
}
cout<<"\n\n";
}
}
};
class Solver{
public:
Board board;
// List of shapes still pending to be inserted on the board
unordered_set<Shape, ShapeHash> shapes;
vector<Board> solutions;
Solver(Board board){
this->board = board;
}
void addShape(Shape& shape){
shapes.insert(shape);
}
void backtrack(Board& board, unordered_set<Shape, ShapeHash>& shapesLeft){
if(shapes.size()==0){
solutions.push_back(board);
return;
}
Shape s = *shapesLeft.begin();
shapesLeft.erase(s);
vector<OrientedShape> oshapes = s.getAllOrientations();
for(int i=0;i<board.height;i++){
for(int j=0;j<board.height-i;j++){
for(int k=0;k<board.height-i;k++){
for(auto oshape: oshapes){
// cout<<i<<" "<<j<<" "<<k<<" "<<s.id<<" "<<shapesLeft.size()<<"\n";
if(not board.canFit(i, j, k, oshape)) continue;
board.fit(i, j, k, s.id, oshape);
backtrack(board, shapesLeft);
board.remove(i, j, k, oshape);
}
}
}
}
shapesLeft.insert(s);
}
void solve(){
solutions.clear();
backtrack(board, shapes);
if(solutions.size()==0){
cout<<"No solution found for the given board and shapes configurations!\n";
return;
}
cout<<solutions.size()<<" solution(s) found! One of the solution is shown below: \n";
solutions[0].print();
}
void printAllSolutions(){
cout<<"There are a total of "<<solutions.size()<<" available solutions for the provided configuration.\n";
for(int i=0;i<solutions.size();i++){
cout<<"Solution "<<i+1<<": \n";
solutions[i].print();
}
}
};
int main(){
Board board(2);
Shape s1(1, 1, 2), s2(2, 1, 1), s3(3, 1, 2);
s1.add(0, 0);
s1.add(0, 1);
s3.add(0, 0);
s3.add(0, 1);
s2.add(0, 0);
Solver solver(board);
solver.addShape(s1);
solver.addShape(s2);
solver.addShape(s3);
solver.solve();
solver.printAllSolutions();
}