nonlinear optimization – Linear solver to select row items with non linear constraints

I’m trying to find a linear combination in T that would satisfy any arbitrary vector s whilst minimising n.

T is composed of n and d and can be represented as follows:

import numpy as np

n = np.random.uniform(-200*10**6,200*10**6,(100))
d = np.multiply(np.random.uniform(-1,1,(100,12)), n(:,np.newaxis))
T = np.concatenate((n(:,np.newaxis),d),axis=1)

while s is arbitrary:

s = np.random.uniform(-1,1,(12))*10**6

In essence, the optimisation problem is solving for vector β such that:

dβ = s where {0 <= β <= 1}

s.t:
min(Σ|nβ|)

I know that if I don’t have any constraints then the problem is simply OLS (I have a tolerance to errors).

But I’m trying to find a method that accommodate this minimisation that doesn’t involve iterative brute force.
Similarly, the constraint for β could also be changed into negative scalars or even bigger/smaller than 1 /-1 depending on the situation or tolerance.

Any tips or direction are very much appreciated!

Thanks all!

bayesian – Bayes probabilty algorithms solver

I have searched the internet and I would like to know if I can use some calculators. Those calculatos are for Bayes. As long as I understand there Naive Bayes and Bayes it is the same with some small difference.What should I change so it will give me the Naive Bayes. If I use those calculatos can give me the results to Naive Bayes.

Calculators of Bayes and I want to produce the result to Naive Bayes.
http://psych.fullerton.edu/mbirnbaum/bayes/BayesCalc.htm
https://stattrek.com/online-calculator/bayes-rule-calculator.aspx

I check that : https://stats.stackexchange.com/questions/444994/naive-bayes-calculation

but he does a lot of calculation.I want to avoid so much calculations.That’s why I want to use the calculator.I beleive second link is a better calculator.
Lets suppose I have those things

P(s|No)=1/5

P(s|Yes)=2/9

P(f|No)=1/5

P(f|Yes)=7/9

P(fr|No)=3/5

P(fr|Yes)=0/9

How I put those here?

enter image description here

and how I calculate the result so it will give me the Naive Bayes

javascript – Sudoku Solver with hashmaps

is there a way to change the board using hashmaps?
There’s this project I have where I have to implement both recursion to solve the board which is already done and solve the board but I need hashmaps to make the board and I’m not sure how to do so.
If anyone can make it and show me it that would be great.

    public static int()() GRID_TO_SOLVE = {
            {9,0,0,1,0,0,0,0,5},
            {0,0,5,0,9,0,2,0,1},
            {8,0,0,0,4,0,0,0,0},
            {0,0,0,0,8,0,0,0,0},
            {0,0,0,7,0,0,0,0,0},
            {0,0,0,0,2,6,0,0,9},
            {2,0,0,3,0,0,0,0,6},
            {0,0,0,2,0,0,9,0,0},
            {0,0,1,9,0,4,5,7,0},
    };
    
    private int()() board;
    public static final int EMPTY = 0; 
    public static final int SIZE = 9; 
    
    public Sudoku(int()() board) {
        this.board = new int(SIZE)(SIZE);
        
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                this.board(i)(j) = board(i)(j);
            }
        }
    }
    
    private boolean isInRow(int row, int number) {
        for (int i = 0; i < SIZE; i++)
            if (board(row)(i) == number)
                return true;
        
        return false;
    }
    
    private boolean isInCol(int col, int number) {
        for (int i = 0; i < SIZE; i++)
            if (board(i)(col) == number)
                return true;
        
        return false;
    }
    
    private boolean isInBox(int row, int col, int number) {
        int r = row - row % 3;
        int c = col - col % 3;
        
        for (int i = r; i < r + 3; i++)
            for (int j = c; j < c + 3; j++)
                if (board(i)(j) == number)
                    return true;
        
        return false;
    }
    
    private boolean isOk(int row, int col, int number) {
        return !isInRow(row, number)  &&  !isInCol(col, number)  &&  !isInBox(row, col, number);
    }
    
       public boolean solve() {
        for (int row = 0; row < SIZE; row++) {
         for (int col = 0; col < SIZE; col++) {
          if (board(row)(col) == EMPTY) {
            for (int number = 1; number <= SIZE; number++) {
              if (isOk(row, col, number)) {
                board(row)(col) = number;

                if (solve()) { 
                  return true;
                } else { 
                  board(row)(col) = EMPTY;
                }
             }
            }

            return false; 
           }
          }
         }

         return true; 
    }
    
    public void display() {
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                System.out.print(" " + board(i)(j));
            }
        
            System.out.println();
        }
        
        System.out.println();
    }
    
    public static void main(String() args) {
        Sudoku sudoku = new Sudoku(GRID_TO_SOLVE);
        System.out.println("Sudoku Grid to Solve");
        sudoku.display();
        
        if (sudoku.solve()) {   
            System.out.println("Solved Grid");
            sudoku.display();
        } else {
            System.out.println("No Solution");
        }
    }

}

python wordgame solver in 270 lines

The live version is at my github

Lingo is a game where the host secretly picks a 5 letter word, then provides the first letter to the player. The player then guesses a word, and the host gives feedback on what letters are right, wrong, or in the wrong position.

I call this feedback match_string and use the following format:

‘s’ = right letter, right position (to represent square)

‘o’ = right letter, wrong position (to represent circle)

‘x’ = letter is not in word. (to represent.. well.. X)

This is a cheater for Lingo. It loads data from the word list (in this case scrabble dictionary) to find potential words. It also provides guesses according to both the probability of characters in remaining words, and also an english word usage frequency.

from copy import copy
from math import log
from random import choices
import re
import os
import pickle

# VARIABLES

num_loops = 10**4

word_len = 5
max_guesses = 5

word_list = 'Collins Scrabble Words (2019).txt'
freq_list = 'all.num.o5.txt'
cache_file = 'cached.pickle'

# PROGRAM


def main():
    wl = WordList()

    # TODO: add a mode where human can play vs comp supplied words
    print("""
1. (H)uman enters guesses and match strings from an external source
2. (C)omputer plays vs itself""")
    while True:
        i = input('Choice?').lower()

        if i in ('1', 'h'):
            human_player(wl)
        elif i in ('2', 'c', ''):
            CompPlay(wl).cp_main()
            break
        else:
            print('Invalid Choice')


def human_player(wl):
    while True:
        first_letter = input('What's the first letter?').upper()

        pc = PossCalculator(wl, first_letter)

        while True:
            pc.print_best(5)

            guess = input('Guess?').upper()
            if guess == '':
                guess = first_letter + pc.get_best(1)(0)(0)
                print(f'Guessing: {guess}')
            elif guess(1:) not in pc.poss:
                print(guess, 'is not a valid word. Please try again')
                continue

            match_string = input('Match String?').lower()
            if not re.search(r'(sox){'+str(word_len)+'}', match_string):
                print('invalid match string. Please try again')

            num_poss = pc.calc_matches(guess, match_string)

            if num_poss == 1:
                print(f'  -={guess}=-')
                break

            print(f'  {num_poss} words left')

            if num_poss == 0:
                print('  WTF did you do?')
                break


def str_pos_sub(string, pos, sub):
    return string(:pos) + sub + string(pos + 1:)


class CompPlay:
    def __init__(self, wl):
        self.wl = wl

    def cp_main(self):
        guess_counter = Counter()
        for _ in range(num_loops):
            word = self.get_word()(0)
            print(f'Word is: {word}')
            pc = PossCalculator(self.wl, word(0))

            guesses = ()
            while True:
                guess = word(0) + pc.get_best(1)(0)(0)
                if guess in guesses:
                    pc.poss.discard(guess(1:))
                    continue

                guesses.append(guess)


                if len(guesses) > max_guesses:
                    print('  :( too many guesses')
                    guess_counter('DQ') += 1
                    break
                elif guess == word:
                    print(f'  -={word}=-')
                    print(f'   {len(guesses)} guesses')
                    guess_counter(len(guesses)) += 1
                    break

                match_string = self.get_match_string(word, guess)
                num_poss = pc.calc_matches(guess, match_string)

                print(f'    {guess}t{match_string}t{num_poss} words left')

                if word(1:) not in pc.poss:
                    print('  WTF did you do?')
                    guess_counter('WTF') += 1
                    break

        print('n')
        for guesses, count in guess_counter.most_common():
            print(f'{count:5d} solved in {guesses} guesses')

    def get_match_string(self, word, guess):
        match_string = '.' * word_len
        for pos in range(word_len):
            if guess(pos) == word(pos):
                match_string = str_pos_sub(match_string, pos, 's')
                word = word.replace(word(pos), '.', 1)

        for pos in range(word_len):
            if match_string(pos) != '.':
                continue
            elif guess(pos) in word(1:):
                match_string = str_pos_sub(match_string, pos, 'o')
                word = word.replace(guess(pos), '.', 1)
            else:
                match_string = str_pos_sub(match_string, pos, 'x')

        return match_string

    def get_word(self):
        return choices(
            list(self.wl.word_freq.keys()),  # population
            list(self.wl.word_freq.values()),  # weights  # TODO: speedup by turning this into a cached cumulative list
        )


class PossCalculator:
    def __init__(self, wl, first_letter):
        self.wl = wl
        self.first_letter = first_letter
        self.poss = copy(wl.starts_with(first_letter))
        print(f' starting letter {first_letter}, {len(self.poss)} words left')

    def calc_matches(self, guess, match_string):
        guess = guess(1:)
        match_string = match_string(1:)

        poss_copy = copy(self.poss)
        for word in poss_copy:
            if not self.check_valid(guess, match_string, word):
                self.poss.remove(word)

        return len(self.poss)

    def check_valid(self, guess, match_string, word):
        pos_dict = {
            's': (),
            'o': (),
            'x': (),
        }

        for pos, char in enumerate(match_string):
            pos_dict(char).append(pos)

        for pos in pos_dict('s'):
            if guess(pos) == word(pos):
                word = str_pos_sub(word, pos, '.')
            else:
                return False

        for pos in pos_dict('o'):
            if guess(pos) in word and guess(pos) != word(pos):
                word = word.replace(guess(pos), '.', 1)
            else:
                return False

        for pos in pos_dict('x'):
            if guess(pos) in word:
                return False

        # You have passed the three trials of the match_string. You have proven yourself.
        return True

    def get_best(self, n):
        char_score = Counter()
        for word in self.poss:
            for char in set(word):
                char_score(char) += 1

        word_scores = Counter()
        for word in self.poss:
            word_set = set(word)
            for char in word_set:
                word_scores(word) += char_score(char)

            word_scores(word) *= (len(word_set) + 1)

        avg_word_score = int(sum(word_scores.values()) / len(word_scores))

        for word, score in word_scores.items():
            word_scores(word) = int(score / avg_word_score * 130)
            word_scores(word) += self.wl.word_freq(self.first_letter + word)

        return word_scores.most_common(n)

    def print_best(self, n):
        for word, score in self.get_best(n):
            print(f'{self.first_letter}{word}t{score}')


class WordList:
    def __init__(self):
        if os.path.exists(cache_file):
            print('Loading cached wordlist!')  # TODO: pickle doesn't want to dump the variables, see below
            # with open(cache_file, 'rb') as f:
            #     self.word_dict = pickle.load(f)
            #     self.word_freq = pickle.load(f)
        else:
            print('Building wordlist!')
            self.build_wordlists()


    def build_wordlists(self):

        self.word_dict = defaultdict(set)
        # word_dict is {first_letter: (rest_of_word1, rest_of_word2)}

        with open(word_list) as f:
            for word in f:
                if len(word) == word_len+1:
                    self.word_dict(word(0)).add(word(1:word_len))
                    # we already know the first letter, so cut off with (1:)
                    # there's a newline while reading, so cut it off with (:5)

        self.word_freq = defaultdict(lambda: 40)

        with open(freq_list) as f:
            for line in f:
                line = line.split()
                if len(line(1)) == word_len:
                    word = line(1).upper()
                    if word(1:) in self.word_dict(word(0)):
                        self.word_freq(word) = int(log(int(line(0)), 6) * 40)

        for word in self.word_freq:
            assert word(1:) in self.word_dict(word(0))

        # with open(cache_file, 'wb') as f:
        #     pickle.dump((self.word_dict, self.word_freq), f)

    def starts_with(self, first_letter):
        return self.word_dict(first_letter)


if __name__ == '__main__':
    main()

html – Python Rubiks cube solver – general code comments

I have been learning Ptyhon/Flask for the past 5 months and have just finished a first complete version of the app:

The app mostly uses Python, Flask and Bootstrap, with a small amount of JavaScript.

Please can I ask for your comments on the overall code, please chose whichever sections you would like to comment on. I am interested to know if there are best-practices or better ways to implement what I have done.

I am aware that I have not used ‘objects’ as much I could have in the python files – I do intend to amend the code to use objects in the near future. I have only included code from the main app.py file below due to Stack Exchange code length limit, so for full context please refer to the github link if you would like. Thanks.

Main app:

# Standard library modules
import os
import datetime
import random
import sqlite3
from tempfile import mkdtemp
from functools import wraps

# Non-standard modules
from flask import Flask, flash, redirect, render_template, request, session
from flask_caching import Cache
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
import pylibmc
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
from werkzeug.security import check_password_hash, generate_password_hash

# Other files within application
import helpers # Contains functions to support the main routes.
import config # Contains large or repeatedly used data.

# Configure application
app = Flask(__name__)

# Auto-reload templates
app.config("TEMPLATES_AUTO_RELOAD") = True

# Ensure responses aren't cached. Sourced from CS50.
@app.after_request
def after_request(response):
    response.headers("Cache-Control") = "no-cache, no-store, must-revalidate"
    response.headers("Expires") = 0
    response.headers("Pragma") = "no-cache"
    return response

# Configure Memcache for session storage, following this Heroku guide:
# https://devcenter.heroku.com/articles/flask-memcache
cache = Cache()
cache_servers = os.environ.get('MEMCACHIER_SERVERS')
if cache_servers == None:
    cache.init_app(app, config={'CACHE_TYPE': 'simple'})
else:
    cache_user = os.environ.get('MEMCACHIER_USERNAME') or ''
    cache_pass = os.environ.get('MEMCACHIER_PASSWORD') or ''
    cache.init_app(app,
        config={'CACHE_TYPE': 'saslmemcached',
                'CACHE_MEMCACHED_SERVERS': cache_servers.split(','),
                'CACHE_MEMCACHED_USERNAME': cache_user,
                'CACHE_MEMCACHED_PASSWORD': cache_pass,
                'CACHE_OPTIONS': { 'behaviors': {
                    # Faster IO
                    'tcp_nodelay': True,
                    # Keep connection alive
                    'tcp_keepalive': True,
                    # Timeout for set/get requests
                    'connect_timeout': 2000, # ms
                    'send_timeout': 750 * 1000, # us
                    'receive_timeout': 750 * 1000, # us
                    '_poll_timeout': 2000, # ms
                    # Better failover
                    'ketama': True,
                    'remove_failed': 1,
                    'retry_timeout': 2,
                    'dead_timeout': 30}}})
    app.config.update(
        SESSION_TYPE = 'memcached',
        SESSION_MEMCACHED =
            pylibmc.Client(cache_servers.split(','), binary=True,
                            username=cache_user, password=cache_pass,
                            behaviors={
                                # Faster IO
                                'tcp_nodelay': True,
                                # Keep connection alive
                                'tcp_keepalive': True,
                                # Timeout for set/get requests
                                'connect_timeout': 2000, # ms
                                'send_timeout': 750 * 1000, # us
                                'receive_timeout': 750 * 1000, # us
                                '_poll_timeout': 2000, # ms
                                # Better failover
                                'ketama': True,
                                'remove_failed': 1,
                                'retry_timeout': 2,
                                'dead_timeout': 30,
                            })
    )
Session(app)

# TEMPORARY FOR FLASK LOCAL TESTING ONLY.
# Configure session to use filesystem,
# (instead of signed cookies), sourced from CS50.
# app.config("SESSION_FILE_DIR") = mkdtemp()
# app.config("SESSION_PERMANENT") = False
# app.config("SESSION_TYPE") = "filesystem"
# Session(app)

# SQLalchemy has removed support for postgress:// scheme which is used
# by Heroku Postgres, the below maintains compatibility.
# https://help.heroku.com/ZKNTJQSK/why-is-sqlalchemy-1-4-x-not-connecting-to-heroku-postgres
uri = os.getenv("DATABASE_URL")
if uri.startswith("postgres://"):
    uri = uri.replace("postgres://", "postgresql://", 1)

# Connect to database.
engine = create_engine(uri)
db = scoped_session(sessionmaker(bind=engine))

# Commit and close database.
def db_close():
    db.commit()
    db.close()

# Check user is logged in, sourced from CS50.
def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if session.get("user_id") is None:
            print("LOGIN_REQUIRED - user_id is None.")
            return redirect("/login")
        return f(*args, **kwargs)
    return decorated_function


# Login an already registered user.
@app.route("/login", methods=("GET", "POST"))
def login():
    # Clear previous user_id.
    session.clear()

    # If reached by get, present login form.
    if request.method == "GET":
        return render_template("login.html")

    # If reached by post, check input and then log the user in.
    if request.method == "POST":
        # Ensure username was entered.
        if not request.form.get("username"):
            flash("Username was not entered, please enter a username.")
            return render_template("/login.html")
        else:
            username = request.form.get("username")
        # Ensure password was entered.
        if not request.form.get("password"):
            flash("Password was not entered, please enter password.")
            return render_template("/login.html")
        # Check database for username and password.
        SQL = "SELECT * FROM users WHERE username = (:username)"
        data = {"username": username}
        rows = db.execute(SQL, data).fetchall()
        # Commit & close database connection.
        db_close()
        # Check if password matches or if no password found in database.
        if len(rows) != 1 or not check_password_hash(rows(0)("hashed_password"), request.form.get("password")):
            flash("Username and/or password does not match.")
            return render_template("/login.html")
        # Remember user has logged in and set current cube to blank.
        session("user_id") = rows(0)("id")
        session("current_cube_id") = 0
        session("username") = username
        # Redirect to homepage.
        return redirect("/")


# Logout the current user.
@app.route("/logout")
def logout():
    # Forget any user_id
    session.clear()
    # Redirect user to login form
    return redirect("/")


# Register a new user.
@app.route("/register", methods=("GET", "POST"))
def register():
    # If user has not submitted form information (arrived via GET).
    if request.method == "GET":
        return render_template("register.html")

    # If user has submitted a form (arrived via POST).
    if request.method == "POST":
        # Check that username has been entered.
        if not request.form.get("username"):
            flash("Username required.")
            return redirect("/register")
        else:
            username = request.form.get("username")
        # Check that username does not already exist.
        SQL = "SELECT username FROM users WHERE username = :username"
        data = {"username": username}
        existing_username = db.execute(SQL, data).fetchall()
        # Close database connection.
        db_close()
        # If username already exists, alert user and return.
        if existing_username:
            flash("Username " + username + " already exists.")
            return redirect("/register")
        # Check that password has been entered.
        password = request.form.get("password")
        if not password:
            flash("Password required.")
            return redirect("/register")
        # Check that second password entry matches the first.
        if not password == request.form.get("confirmation"):
            flash("Passwords do not match.")
            return redirect("/register")
        # Check password has at least 8 characters.
        if len(password) < 8:
            flash("Password must contain at least 8 characters.")
            return redirect("/register")
        # Check password has at least 1 number.
        if not any(character.isdigit() for character in password):
            flash("Password must contain at least 1 number.")
            return redirect("/register")
        # Hash password
        hashed = generate_password_hash(password, "sha256")
        # Submit username and hashed password to database.
        SQL = "INSERT INTO users (username, hashed_password) VALUES (:username, :hashed)"
        data = {"username": username, "hashed": hashed}
        db.execute(SQL, data)
        # Automatically log user in.
        SQL = "SELECT id FROM users WHERE username = (:username)"
        data = {"username": username}
        user_id = db.execute(SQL, data).fetchall()
        session('user_id') = user_id(0)("id")
        session("username") = username
        # Commit & close database connection.
        db_close()
        # Send user to homepage after registration and login complete.
        flash(f"You have been registered successfully with username: {username}.")
        return redirect("/")


# Create a Guest user.
@app.route("/guest")
def guest():
        # Submit generic guest username in order to get a unique user ID.
        SQL = "INSERT INTO users (username) VALUES (:username)"
        data = {"username": "guest tbc"}
        db.execute(SQL, data)
        # Get user ID to include the ID in the guest username.
        SQL = "SELECT id FROM users WHERE username = (:username)"
        data = {"username": "guest tbc"}
        user_rows = db.execute(SQL, data).fetchone()
        user_id = user_rows(0)
        # Update generic guest username to include their user ID.
        new_guest_username = f"Guest {user_id}"
        SQL = "UPDATE users SET (username) = row(:new_guest_username) WHERE (id) = (:user_id)"
        data = ({"new_guest_username": new_guest_username, "user_id": user_id})
        db.execute(SQL, data)
        # Automatically log user in.
        session('user_id') = user_id
        session("username") = new_guest_username
        # Commit & close database connection.
        db_close()
        # Send user to homepage after registration and login complete.
        return redirect("/")


# Load this users cubes, ready for display.
def load_users_cubes():
    # Display previously entered cubes for this user.
    SQL = "SELECT * FROM cubes WHERE user_id = (:user_id) ORDER BY id DESC"
    data = {"user_id": session("user_id")}
    users_cubes = db.execute(SQL, data).fetchall()
    # Close database connection.
    db_close()
    return users_cubes


# Default route, homepage.
@app.route("/")
@login_required
def index():
    # Load users cubes and render homepage.
    users_cubes = load_users_cubes()
    return render_template("index.html", users_cubes=users_cubes)


# Display only loading table.
@app.route("/load_page", methods=("GET"))
@login_required
def load_page():
    # Load users cubes and render load page.
    users_cubes = load_users_cubes()
    return render_template("load.html", users_cubes=users_cubes)


# Route to delete existing cube.
@app.route("/delete_cube", methods=("POST"))
@login_required
def delete_cube():
    # Delete the selected cube from the database.
    cube_to_delete = request.form.get("delete")
    SQL = "DELETE FROM cubes WHERE id = :cube_to_delete"
    data = {"cube_to_delete": cube_to_delete}
    db.execute(SQL, data)
    # Commit & close database connection.
    db_close()
    # If sessions current cube is the cube to be deleted, set current cube to zero.
    if session("current_cube_id") == cube_to_delete:
        session("current_cube_id") = 0
    # Alert user to successful deletion and return to homepage.
    flash("Cube ID " + cube_to_delete + " successfully deleted.")
    return redirect("/")


# Delete all cubes of this user.
@app.route("/delete_all_cubes", methods=("GET"))
@login_required
def delete_all_cubes():
    # Delete all cubes that belong to this user.
    SQL = "DELETE FROM cubes WHERE user_id = :user_id"
    data = {"user_id": session("user_id")}
    db.execute(SQL, data)
    # Commit & close database connection.
    db_close()
    # Set current cube to zero.
    session("current_cube_id") = 0
    # Alert user to successful deletion and return to homepage.
    flash("All your cubes have now been successfully deleted.")
    return redirect("/")


# Create blank cube and store ID
def create_cube():
    # Determine time/date cube created, converted to users local time.
    now = datetime.datetime.now()
    created = now.strftime("%Y/%m/%d %H:%M:%S")
    # Create new cube in database to generate cube ID number.
    SQL = "INSERT INTO cubes (user_id, created) VALUES (:user_id, :created)"
    data = {"user_id": session("user_id"), "created": created}
    db.execute(SQL, data)
    # Find the ID of the cube that has just been created.
    SQL = "SELECT id FROM cubes WHERE user_id = :user_id ORDER BY created DESC LIMIT 1"
    data = {"user_id": session("user_id")}
    cube_id_list = db.execute(SQL, data).fetchall()
    # Commit & close database connection.
    db_close()
    # Store Id of this new cube and clear the session cube.
    cube_id = cube_id_list(0)('id')
    session("current_cube_id") = cube_id
    session("cube") = ()


# Check if cube is valid (correct number of colours accounted for):
def check_cube():
    # Create colour_check dictionary and set counts to zero.
    colour_check = dict.fromkeys(config.colours)
    for colour in colour_check:
        colour_check(colour) = 0

    # Sum the total number of each colour.
    print("SESSION CUBE:")
    print(session("cube"))
    for square in session("cube"):
        for colour in config.colours:
            if session("cube")(square) == colour:
                colour_check(colour) = colour_check(colour) + 1
                break

    # Initiate the list of errors, to be populated next.
    session("errors") = ()

    # Check if colour totals are too many or too few.
    for colour in colour_check:
        # There should be 9 of each colour.
        if colour_check(colour) == 9:
            continue
        elif colour_check(colour) < 9:
            session("errors").append("Too few " + colour + " squares.")
            continue
        elif colour_check(colour) > 9:
            session("errors").append("Too many " + colour + " squares.")

    # If no errors, confirm the cube is correct and proceed.
    if not session("errors"):
        # Add "no errors" & progress status to database.
        progress = helpers.solve_progress(session("cube"))
        SQL = "UPDATE cubes SET (input_check, stage) = ('Input Ok', :stage) WHERE (id) = (:cube_id);"
        data = ({"stage": progress, "cube_id": session("current_cube_id")}, )
        db.execute(SQL, data)
        # Commit & close database connection.
        db_close()
        print("CHECK_CUBE - Completed with no errors.")
        return redirect("/solve")

    # If errors found, list those errors and ask user to correct.
    else:
        # Load "amend" page to display errors and resolve them.
        SQL = "UPDATE cubes SET (input_check) = row('Error') WHERE (id) = (:cube_id);"
        data = ({"message": 'Error', "cube_id": session("current_cube_id")})
        db.execute(SQL, data)
        # Commit & close database connection.
        db_close()
        # Flash message to advise that error needs to be resolved.
        flash("Errors found in your cube entry, please resolve before solving.")
        print("CHECK CUBE - Errors found.")
        return redirect("/amend")


# Try to solve with random moves (it won't solve the cube).
@app.route("/solve_randomly")
@login_required
def solve_randomly():
    # Load session cube, define number of moves & initialise counter.
    cube = session("cube")
    max_number_of_moves = 100000
    move_count = 0
    # Make random moves until cube is solved or move limit reached.
    for move in range(0, max_number_of_moves):
        # If cube is solved, end for loop.
        if helpers.solve_progress(cube) == 8:
            break
        # If cube is not solved, make random move to cube.
        else:
            move_count = move_count + 1
            cube = helpers.random_move(cube)
    # Update session cube with resulting cube.
    for square in config.squares:
        session("cube")(square) = cube(square)
    # Advise user moves were made and return.
    flash(str(move_count) + " randomly picked moves were made.")
    return redirect("/solve")


# Provide random moves to user, for user to randomise real-life cube.
# This will generte a list of moves to make; not amend the cube itself.
# Number of random moves to make will default to 30 unless flask url
# argument received to state otherwise.
@app.route("/randomise_user_cube", defaults={"random_moves":30})
@app.route("/randomise_user_cube/<int:random_moves>")
@login_required
def randomise_user_cube(random_moves):
    # Initialise moves list & move counter.
    random_moves_list = ()
    move_count = 0
    # Randomly make moves to cube until max count reached.
    while move_count < random_moves:
        # Randomly select a number between 0 and 11 inclusive.
        y = random.randint(0, 11)
        # Use random number to select a move from list of moves.
        move = config.possible_moves(y)
        # Add move to list of moves to make.
        random_moves_list.append(move)
        # Remove uncesseray/inefficient moves.
        random_moves_list = helpers.improve_efficiency(random_moves_list)
        # Update move_count.
        move_count = len(random_moves_list)
    # Return list of random moves.
    return render_template("randomiser.html", random_moves_list=random_moves_list)


# Randomise a solved cube to ensure it can actually be solved.
@app.route("/random_cube")
@login_required
def random_cube():
    # Load solved cube.
    cube = config.solved_cube
    # Define number of random moves to make.
    random_moves = 50
    # Make random moves.
    for x in range(0, random_moves):
        cube = helpers.random_move(cube)
    # Create new cube in databse:
    create_cube()
    # Enter the randomised data into the dictionary:
    for square in cube:
        # Update database with colour of each square. There may be a
        # better way to update 54 SQL columns, to be investigated.
        # Note that as the input to the below SQL query is hard-coded,
        # there should be no risk of SQL injection attack.
        SQL = f"UPDATE cubes SET {square} = :square WHERE id = :cube_id"
        data = {"square": cube(square), "cube_id": session("current_cube_id")}
        db.execute(SQL, data)
    # Commit & close database connection.
    db_close()
    # Update session cube & return cube check:
    session("cube") = cube
    print("RANDOM CUBE - Function complete.")
    return check_cube()


# Route to create new cube.
@app.route("/enter", methods=("GET", "POST"))
@login_required
def enter():

    if request.method == "GET":
        # Display blank template to entre new cube.
        return render_template("enter.html", squares=config.squares, colour_initials=config.colour_initials)

    if request.method == "POST":
        # Create empty dictionary of squares, ready for user input.
        cube = dict.fromkeys(config.squares)
        # Create new cube in database:
        create_cube()
        # Enter the submitted data into the dictionary:
        # Note that as the input to the below SQL query is hard-coded,
        # there should be no risk of SQL injection attack.
        for square in cube:
            square_colour = request.form.get(square)
            cube(square) = square_colour
            SQL = f"UPDATE cubes SET {square} = :square_colour WHERE id = :cube_id"
            data = {"square_colour": square_colour, "cube_id": session("current_cube_id")}
            db.execute(SQL, data)
        # Commit & close database connection.
        db_close()
        # Update session cube & return cube check:
        session("cube") = cube
        return check_cube()


@app.route("/load", methods=("POST"))
@login_required
def load():
    # Replace current session cube id with the clicked cube_id.
    cube_to_load = request.form.get("load")
    session("current_cube_id") = cube_to_load
    # Load cube from database into session cube.
    SQL = "SELECT * FROM cubes WHERE id = :cube_to_load"
    data = {"cube_to_load": cube_to_load}
    cube_loading = db.execute(SQL, data)
    # Convert to dictionary from row object to allow lookup by key.
    list = (dict(row) for row in cube_loading)
    session("cube") = list(0)
    # Commit & close database connection.
    db_close()
    return check_cube()


@app.route("/amend_from_list", methods=("POST"))
@login_required
def amend_from_list():
    # Replace current session cube with clicked cube.
    cube_to_amend = request.form.get("amend")
    session("current_cube_id") = cube_to_amend
    # Load cube from database into session cube.
    SQL = "SELECT * FROM cubes WHERE id = :cube_to_amend"
    data = {"cube_to_amend": cube_to_amend}
    cube_loading = db.execute(SQL, data).fetchall()
    session("cube") = cube_loading(0)
    # Commit & close database connection.
    db_close()
    return redirect("/amend")


@app.route("/copy", methods=("POST"))
@login_required
def copy():
    # Load cube to be copied into temporary dictionary.
    cube_id_to_copy = request.form.get("copy")
    # Load cube from database into temp cube.
    SQL = "SELECT * FROM cubes WHERE id = :cube_id_to_copy"
    data = {"cube_id_to_copy": cube_id_to_copy}
    cube_loading = db.execute(SQL, data)
    # Convert to dictionary from row object to allow lookup by key.
    list = (dict(row) for row in cube_loading)
    temp_cube = list(0)
    # Create new blank cube, and make current session cube.
    create_cube()
    # Populate curret session cube with previous cube contents.
    session("cube") = temp_cube
    session("cube")("id") = session("current_cube_id")
    # Save new cube contents to database.
    # Note that as the input to the below SQL query is hard-coded,
    # there should be no risk of SQL injection attack.
    for item in temp_cube:
        SQL = f"UPDATE cubes SET {item} = (:cube_item) WHERE (id) = (:cube_id)"
        data = {"cube_item": session("cube")(item), "cube_id": session("current_cube_id")}
        db.execute(SQL, data)
    # Commit & close database connection.
    db_close()
    # Flash message to user then proceed to home page.
    flash("Copy of Cube ID " + str(cube_id_to_copy) + ", created as new Cube ID " + str(session("current_cube_id")))
    return redirect("/")


@app.route("/amend", methods=("GET", "POST"))
@login_required
def amend():
    # If loading page prior to data entry,
    # display squares based on current session cube.
    if request.method == "GET":
        return render_template("amend.html", squares=config.squares, colour_initials=config.colour_initials, cube=session("cube"))

    # If new data submitted:
    if request.method == "POST":
        # Update database with user input from form.
        # Note that as the input to the below SQL query is hard-coded,
        # there should be no risk of SQL injection attack.
        cube = session("cube")
        for square in config.squares:
            square_colour = request.form.get(square)
            cube(square) = square_colour
            SQL = f"UPDATE cubes SET {square} = (:colour) WHERE (id) = (:cube_id);"
            data = ({"colour": square_colour, "cube_id": session("current_cube_id")})
            db.execute(SQL, data)
        # Commit & close database connection.
        db_close()
        # Update session cube & return cube check:
        session("cube") = cube
        return check_cube()


@app.route("/solve", methods=("POST", "GET"))
@login_required
def solve():
    print("SOLVE - Start solve function.")
    # Take current session cube and check progress.
    current_cube_id = session("current_cube_id")
    progress = helpers.solve_progress(session("cube"))
    # Update solve progress in database.
    SQL = "UPDATE cubes SET (stage) = row(:progress) WHERE (id) = (:cube_id);"
    data = ({"progress": progress, "cube_id": session("current_cube_id")}, )
    db.execute(SQL, data)
    # Commit & close database connection.
    db_close()
    print("SOLVE - Progress stage found to be " + str(progress))
    
    # If cube is completed already, show 'complete' page.
    if progress == 8:
        return render_template("complete.html")

    # Else if cube is not solved, determine next move required.
    else:
        # Start with "next_cube_colours" matching current cube, ready
        # for moves to be mdae.
        session("next_cube_colours") = session("cube")
        # Create list of moves required to
        # progress to solve the current stage of the cube.
        next_actions_list = helpers.next_action()
        # Improve efficiency of moves in next_actions_list.
        next_actions_list = helpers.improve_efficiency(next_actions_list)
        # Look up nick name of current stage.
        stage_name = config.stage_names(progress)
        # Render solve page.
        return render_template("solve.html", next_actions_list=next_actions_list, squares=config.squares, cube=session("cube"), next_cube=session("next_cube_colours"), current_cube_id=session("current_cube_id"), progress=progress, stage_name=stage_name)


@app.route("/solve_entirely")
@login_required
def solve_entirely():
    print("SOLVE ENTIRELY - Single stage solve function started.")
    # Initialise list for required moves.
    complete_solve_list = ()
    # Prepare temp dictionary.
    session("next_cube_colours") = session("cube")
    # Take current session cube and check progress.
    current_cube_id = session("current_cube_id")
    progress = helpers.solve_progress(session("cube"))
    # Record stage at which cube started, to correctly display
    # the progress bar and stage description on the page.
    starting_progress = progress

    # If cube is completed already, show 'complete' page.
    if progress == 8:
        print("SOLVE ENTIRELY - Solving stage is 8.")
        # Cube already solved, nothing to do.
        return render_template("complete.html")

    # Else if cube is before the final stage, continue to loop round, appending to
    # list of moves required until cube is solved.
    while progress < 7:
        print("SOLVE ENTIRELY - Solving stage less than 7.")
        # Loop through each solve stage.
        # Append moves required for that solve stage to the overall list.
        next_moves_list = helpers.next_action()
        for move in next_moves_list:
            complete_solve_list.append(move)
        # Check if the above moves have solved the cube,
        # in order to break the while loop.
        progress = helpers.solve_progress(session("next_cube_colours"))

    # Else the stage must be 7 (last stage) so determine moves for
    # final stage then render page with list of moves.
    else:
        print("SOLVE ENTIRELY - Solving stage is equal to 7.")
        # Append moves required for that solve stage to the overall list.
        next_moves_list = helpers.next_action()
        for move in next_moves_list:
            complete_solve_list.append(move)
        # Improve efficiency of moves in next_actions_list and render.
        next_actions_list = helpers.improve_efficiency(complete_solve_list)
        # Look up nick name of current stage.
        stage_name = config.stage_names(progress)
        # Render solve page.
        return render_template("solve.html", next_actions_list=complete_solve_list, squares=config.squares, cube=session("cube"), next_cube=session("next_cube_colours"), current_cube_id=session("current_cube_id"), progress=starting_progress, stage_name=stage_name)


# Function to record the user has correctly followed the moves of this stage,
# and loop back into the solve function.
@app.route("/next_stage")
@login_required
def next_stage():
    # User has confirmed that they made the moves correctly, so
    # update session cube with next_cube_colours, then allowing
    # the solve to continue from that point. 
    for square in config.squares:
        session("cube")(square) = session("next_cube_colours")(square)
        # Update the database with new cube state.
        # Note that as the input to the below SQL query is hard-coded,
        # there should be no risk of SQL injection attack.
        SQL = f"UPDATE cubes SET {square} = (:colour) WHERE (id) = (:cube_id)"
        data = ({"square": square, "colour": session('cube')(square), "cube_id": session("current_cube_id")})
        db.execute(SQL, data)
    # Commit & close database connection.
    db_close()
    print("NEXT_STAGE - Function complete.")
    # Return to solve page to solve this new cube state.
    return redirect("/solve")

I will be your virtual math teacher problem solvers for $5

I will be your virtual math teacher problem solvers

If you have troubles studying math and preparing for important exams

I am here to assist you by:

– tutoring

– organizing your work load

– teaching how to be successful at learning math

– scheduling your study

– motivate you

I do:

– Algebra

– Trigonometry

– Geometry

– Discrete Math

– Calculus

– Data analysis, statistics and probability

– Special Mathematics

thank you for visiting my gig!!

plz message be before placing the order so that I understand your work more clearly.

.

I will be your math, calculus, discrete, integration solver for $10

I will be your math, calculus, discrete, integration solver

Hey! Are you stuck in solving mathematics problems in your Exercises or Tests or Assignments …… Don’t worry. Here is your way.

I’m Mudasir . I am providing the services of being your Math Solver and help you with solving mathematics problems..

If you are facing any kind of problem in your math : this gig is for you.

I will provide Solutions with detailed explanation(step-by-step)

Abstract Algebra

Calculus i, ii & iii

Discrete Math

Mathematical Mathod

Real Analysis i & ii

Linear Algebra

Numerical Analysis

Complex Analysis

Mathematical Statistics

Ordinary Differential equation

Partial Differential equation

Tigonometry

Number Theory & many more….

But before placing an order, Do contact me and let me know about your task.

Waiting for your order….and ready to serve.

Thank you very much

.(tagsToTranslate)math(t)solver(t)calculus(t)integration(t)Mathematical

c++ – Skyscraper Solver for NxN Size Version 3 (Using Bitmasks)

This is a follow up of Skyscraper Solver for NxN Size Version 2 (Using Backtracking)

I followed the advice in the last Codereview and did the following optimizations:

  • Implementation of the class Field using a single Bitmask wiht
    std::uint32_t to represent Skyscrapers and Nopes. To give you an
    idea for n=4 the representation looks like this:

    b0000 0 Nopes = {}
    b0001 1 Nopes = 1
    b0010 2 Nopes = 2
    b0011 3 Nopes = 1, 2
    b0100 4 Nopes = 3
    b0101 5 Nopes = 1, 3
    b0110 6 Nopes = 2, 3
    b0111 7 Skyscraper = 4
    b1000 8 Nopes = 4
    b1001 9 Nopes = 1, 4
    b1010 10 Nopes = 2, 4
    b1011 11 Skyscraper = 3
    b1100 12 Nopes = 3, 4
    b1101 13 Skyscraper
    b1110 14 Skyscraper = 1
    b1111 15 Invalid all nope
    

    Now the idea was that now that we represent Nopes and Skyscrapers
    with a bitmask the size for each field should go down a lot. Before
    this optimization I used an extra Nope class which is an
    std::unordered_set and an int for representing the Skyscraper.

    So some of the old Nope class methods could get fused into the new Field class saving us
    the extra implementation and keeping track of all the Nope stuff.

  • Storing the fields of the Board in an flat array. So instead of
    std::vector<std::vector<Field>> I know use std::vector<Field>.
    With the size of the puzzle we can still access each two dimensional
    position on the Board.

Now the bad news. With the optimization I was hoping the backtracking would solve the puzzles faster. Unfortunately the opposite happens. Now my solution is even more slow. I wonder if I just implemented the Field class with the Bitmask wrong.

So please let me know what stinks in terms of performance.

The full source code:

#include "codewarsbacktracking.h"

#include <algorithm>
#include <cassert>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <string>
#include <unordered_set>

namespace codewarsbacktracking {

struct ClueHints {
    ClueHints(std::size_t boardSize);
    ClueHints();

    void reverse();

    void removeNopesOnSkyscrapers();

    std::vector<int> skyscrapers{};
    std::vector<std::vector<int>> nopes{};
};

ClueHints::ClueHints(std::size_t boardSize)
    : skyscrapers{std::vector<int>(boardSize, 0)},
      nopes{std::vector<std::vector<int>>(boardSize, std::vector<int>{})}
{
}

void ClueHints::reverse()
{
    std::reverse(skyscrapers.begin(), skyscrapers.end());
    std::reverse(nopes.begin(), nopes.end());
}

void ClueHints::removeNopesOnSkyscrapers()
{
    for (std::size_t i = 0; i < skyscrapers.size(); ++i) {
        if (skyscrapers(i) == 0) {
            continue;
        }
        nopes(i).clear();
    }
}

std::optional<ClueHints> getClueHints(int clue, std::size_t boardSize)
{
    if (clue == 0) {
        return {};
    }

    ClueHints clueHints{boardSize};

    std::vector<std::unordered_set<int>> nopes(boardSize,
                                               std::unordered_set<int>{});

    if (clue == static_cast<int>(boardSize)) {
        for (std::size_t i = 0; i < boardSize; ++i) {
            clueHints.skyscrapers(i) = i + 1;
        }
    }
    else if (clue == 1) {
        clueHints.skyscrapers(0) = boardSize;
    }
    else if (clue == 2) {
        nopes(0).insert(boardSize);
        nopes(1).insert(boardSize - 1);
    }
    else {
        for (std::size_t fieldIdx = 0;
             fieldIdx < static_cast<std::size_t>(clue - 1); ++fieldIdx) {

            for (std::size_t nopeValue = boardSize;
                 nopeValue >= (boardSize - (clue - 2) + fieldIdx);
                 --nopeValue) {
                nopes(fieldIdx).insert(nopeValue);
            }
        }
    }

    assert(nopes.size() == clueHints.nopes.size());

    for (std::size_t i = 0; i < nopes.size(); ++i) {
        clueHints.nopes(i) = std::vector<int>(nopes(i).begin(), nopes(i).end());
    }
    return {clueHints};
}

std::optional<ClueHints> merge(std::optional<ClueHints> optFrontClueHints,
                               std::optional<ClueHints> optBackClueHints)
{
    if (!optFrontClueHints && !optBackClueHints) {
        return {};
    }
    if (!optFrontClueHints) {
        optBackClueHints->reverse();
        return optBackClueHints;
    }
    if (!optBackClueHints) {
        return optFrontClueHints;
    }

    auto size = optFrontClueHints->skyscrapers.size();
    ClueHints clueHints{size};

    assert(optFrontClueHints->skyscrapers.size() ==
           optFrontClueHints->nopes.size());
    assert(optBackClueHints->skyscrapers.size() ==
           optBackClueHints->nopes.size());
    assert(optFrontClueHints->skyscrapers.size() ==
           optBackClueHints->skyscrapers.size());

    optBackClueHints->reverse();

    for (std::size_t i = 0; i < optFrontClueHints->skyscrapers.size(); ++i) {

        auto frontSkyscraper = optFrontClueHints->skyscrapers(i);
        auto backSkyscraper = optBackClueHints->skyscrapers(i);

        if (frontSkyscraper != 0 && backSkyscraper != 0) {
            assert(frontSkyscraper == backSkyscraper);
            clueHints.skyscrapers(i) = frontSkyscraper;
        }
        else if (frontSkyscraper != 0) {
            clueHints.skyscrapers(i) = frontSkyscraper;
            clueHints.nopes(i).clear();
        }
        else { // backSkyscraper != 0
            clueHints.skyscrapers(i) = backSkyscraper;
            clueHints.nopes(i).clear();
        }

        if (clueHints.skyscrapers(i) != 0) {
            continue;
        }

        std::unordered_set<int> nopes(optFrontClueHints->nopes(i).begin(),
                                      optFrontClueHints->nopes(i).end());
        nopes.insert(optBackClueHints->nopes(i).begin(),
                     optBackClueHints->nopes(i).end());
        clueHints.nopes(i) = std::vector<int>(nopes.begin(), nopes.end());
    }
    clueHints.removeNopesOnSkyscrapers();
    return {clueHints};
}

void mergeClueHintsPerRow(std::vector<std::optional<ClueHints>> &clueHints)
{
    std::size_t startOffset = clueHints.size() / 4 * 3 - 1;
    std::size_t offset = startOffset;

    for (std::size_t frontIdx = 0; frontIdx < clueHints.size() / 2;
         ++frontIdx, offset -= 2) {

        if (frontIdx == clueHints.size() / 4) {
            offset = startOffset;
        }

        int backIdx = frontIdx + offset;

        clueHints(frontIdx) = merge(clueHints(frontIdx), clueHints(backIdx));
    }
    clueHints.erase(clueHints.begin() + clueHints.size() / 2, clueHints.end());
}

std::vector<std::optional<ClueHints>>
getClueHints(const std::vector<int> &clues, std::size_t boardSize)
{
    std::vector<std::optional<ClueHints>> clueHints;
    clueHints.reserve(clues.size());

    for (const auto &clue : clues) {
        clueHints.emplace_back(getClueHints(clue, boardSize));
    }
    mergeClueHintsPerRow(clueHints);
    return clueHints;
}

template <typename It> int missingNumberInSequence(It begin, It end)
{
    int n = std::distance(begin, end) + 1;
    double projectedSum = (n + 1) * (n / 2.0);
    int actualSum = std::accumulate(begin, end, 0);
    return projectedSum - actualSum;
}

using BitmaskType = std::uint32_t;

/*
Example size = 4

b0000 0 Nopes = {}
b0001 1 Nopes = 1
b0010 2 Nopes = 2
b0011 3 Nopes = 1, 2
b0100 4 Nopes = 3
b0101 5 Nopes = 1, 3
b0110 6 Nopes = 2, 3
b0111 7 Skyscraper = 4
b1000 8 Nopes = 4
b1001 9 Nopes = 1, 4
b1010 10 Nopes = 2, 4
b1011 11 Skyscraper = 3
b1100 12 Nopes = 3, 4
b1101 13 Skyscraper
b1110 14 Skyscraper = 1
b1111 15 Invalid all nope
*/

class Field {
public:
    Field(std::size_t size);

    void insertSkyscraper(int skyscraper);
    void insertNope(int nope);
    void insertNopes(const std::vector<int> &nopes);

    int skyscraper() const;
    std::vector<int> nopes() const;

    bool hasSkyscraper() const;

    bool containsNope(int value) const;
    bool containsNopes(const std::vector<int> &values);

    BitmaskType bitmask() const;
    void setBitmask(BitmaskType bitmask);

private:
    bool bitIsToggled(BitmaskType bitmask, int bit) const;

    BitmaskType mBitmask{0};
    std::size_t mSize;

    friend inline bool operator==(const Field &lhs, const Field &rhs);
    friend inline bool operator!=(const Field &lhs, const Field &rhs);
};

inline bool operator==(const Field &lhs, const Field &rhs)
{
    return lhs.mBitmask == rhs.mBitmask;
}
inline bool operator!=(const Field &lhs, const Field &rhs)
{
    return !(lhs == rhs);
}

Field::Field(std::size_t size) : mSize{size}
{
}

void Field::insertSkyscraper(int skyscraper)
{
    assert(skyscraper > 0 && skyscraper <= static_cast<int>(mSize));
    BitmaskType mask = 1;
    for (int i = 0; i < static_cast<int>(mSize); ++i) {
        if (i != skyscraper - 1) {
            mBitmask |= mask;
        }
        mask <<= 1;
    }
}

void Field::insertNope(int nope)
{
    assert(nope > 0 && nope <= static_cast<int>(mSize));
    mBitmask |= 1 << (nope - 1);
}

void Field::insertNopes(const std::vector<int> &nopes)
{
    for (const auto nope : nopes) {
        insertNope(nope);
    }
}

int Field::skyscraper() const
{
    if (!hasSkyscraper()) {
        return 0;
    }

    for (std::size_t i = 0; i < mSize; ++i) {
        if (!(bitIsToggled(mBitmask, i))) {
            return i + 1;
        }
    }
    return 0;
}

std::vector<int> Field::nopes() const
{
    std::vector<int> nopes;
    nopes.reserve(mSize - 1);
    for (std::size_t i = 0; i < mSize; ++i) {
        if (bitIsToggled(mBitmask, i)) {
            nopes.emplace_back(i + 1);
        }
    }
    return nopes;
}

bool Field::hasSkyscraper() const
{
    bool foundZero = false;
    for (std::size_t i = 0; i < mSize; ++i) {
        if (!(bitIsToggled(mBitmask, i))) {
            if (!foundZero) {
                foundZero = true;
            }
            else { // found more than one zero so no skyscraper present
                return false;
            }
        }
    }
    return true;
}

bool Field::containsNope(int value) const
{
    return bitIsToggled(mBitmask, value - 1);
}

bool Field::containsNopes(const std::vector<int> &values)
{
    for (const auto &value : values) {
        if (!containsNope(value)) {
            return false;
        }
    }
    return true;
}

BitmaskType Field::bitmask() const
{
    return mBitmask;
}

void Field::setBitmask(BitmaskType bitmask)
{
    mBitmask = bitmask;
}

bool Field::bitIsToggled(BitmaskType bitmask, int bit) const
{
    return bitmask & (1 << bit);
}

struct Point {
    int x;
    int y;
};

inline bool operator==(const Point &lhs, const Point &rhs)
{
    return lhs.x == rhs.x && lhs.y == rhs.y;
}
inline bool operator!=(const Point &lhs, const Point &rhs)
{
    return !(lhs == rhs);
}

enum class ReadDirection { topToBottom, rightToLeft };

void nextDirection(ReadDirection &readDirection);

void advanceToNextPosition(Point &point, ReadDirection readDirection,
                           int clueIdx);

void nextDirection(ReadDirection &readDirection)
{
    assert(readDirection != ReadDirection::rightToLeft);
    int dir = static_cast<int>(readDirection);
    ++dir;
    readDirection = static_cast<ReadDirection>(dir);
}

void advanceToNextPosition(Point &point, ReadDirection readDirection,
                           int clueIdx)
{
    if (clueIdx == 0) {
        return;
    }
    switch (readDirection) {
    case ReadDirection::topToBottom:
        ++point.x;
        break;
    case ReadDirection::rightToLeft:
        ++point.y;
        break;
    }
}

class Row {
public:
    Row(std::vector<Field> &fields, std::size_t size, const Point &startPoint,
        const ReadDirection &readDirection);

    void insertSkyscraper(int pos, int skyscraper);

    std::size_t size() const;

    void addCrossingRows(Row *crossingRow);

    bool hasOnlyOneNopeField() const;
    void addLastMissingSkyscraper();

    void addNopesToAllNopeFields(int nope);

    bool allFieldsContainSkyscraper() const;

    int skyscraperCount() const;
    int nopeCount(int nope) const;

    void guessSkyscraperOutOfNeighbourNopes();

    enum class Direction { front, back };

    bool hasSkyscrapers(const std::vector<int> &skyscrapers,
                        Direction direction) const;
    bool hasNopes(const std::vector<std::vector<int>> &nopes,
                  Direction direction) const;

    void addSkyscrapers(const std::vector<int> &skyscrapers,
                        Direction direction);
    void addNopes(const std::vector<std::vector<int>> &nopes,
                  Direction direction);

    std::vector<Field *> getFields() const;

private:
    template <typename SkyIterator, typename FieldIterator>
    bool hasSkyscrapers(SkyIterator skyItBegin, SkyIterator skyItEnd,
                        FieldIterator fieldItBegin,
                        FieldIterator fieldItEnd) const;

    template <typename NopesIterator, typename FieldIterator>
    bool hasNopes(NopesIterator nopesItBegin, NopesIterator nopesItEnd,
                  FieldIterator fieldItBegin, FieldIterator fieldItEnd) const;

    template <typename SkyIterator, typename FieldIterator>
    void addSkyscrapers(SkyIterator skyItBegin, SkyIterator skyItEnd,
                        FieldIterator fieldItBegin, FieldIterator fieldItEnd);

    template <typename NopesIterator, typename FieldIterator>
    void addNopes(NopesIterator nopesItBegin, NopesIterator nopesItEnd,
                  FieldIterator fieldItBegin, FieldIterator fieldItEnd);

    template <typename IteratorType>
    void insertSkyscraper(IteratorType it, int skyscraper);

    template <typename IteratorType> void insertNope(IteratorType it, int nope);

    template <typename IteratorType>
    void insertNopes(IteratorType it, const std::vector<int> &nopes);

    int getIdx(std::vector<Field *>::const_iterator cit) const;
    int getIdx(std::vector<Field *>::const_reverse_iterator crit) const;

    std::vector<Field *> getRowFields(const ReadDirection &readDirection,
                                      std::vector<Field> &boardFields,
                                      std::size_t size,
                                      const Point &startPoint);

    bool onlyOneFieldWithoutNope(int nope) const;

    bool nopeExistsAsSkyscraperInFields(const std::vector<Field *> &rowFields,
                                        int nope) const;

    std::optional<int> nopeValueInAllButOneField() const;

    void insertSkyscraperToFirstFieldWithoutNope(int nope);

    bool hasSkyscraper(int skyscraper) const;

    std::vector<Row *> mCrossingRows;
    std::vector<Field *> mRowFields;
};

Row::Row(std::vector<Field> &fields, std::size_t size, const Point &startPoint,
         const ReadDirection &readDirection)
    : mRowFields{getRowFields(readDirection, fields, size, startPoint)}
{
}

void Row::insertSkyscraper(int pos, int skyscraper)
{
    assert(pos >= 0 && pos < static_cast<int>(mRowFields.size()));
    assert(skyscraper > 0 && skyscraper <= static_cast<int>(mRowFields.size()));
    auto it = mRowFields.begin() + pos;
    insertSkyscraper(it, skyscraper);
}

std::size_t Row::size() const
{
    return mRowFields.size();
}

void Row::addCrossingRows(Row *crossingRow)
{
    assert(crossingRow != nullptr);
    assert(mCrossingRows.size() < size());
    mCrossingRows.push_back(crossingRow);
}

bool Row::hasOnlyOneNopeField() const
{
    return skyscraperCount() == static_cast<int>(size() - 1);
}

void Row::addLastMissingSkyscraper()
{
    assert(hasOnlyOneNopeField());

    auto nopeFieldIt = mRowFields.end();
    std::vector<int> sequence;
    sequence.reserve(size() - 1);

    for (auto it = mRowFields.begin(); it != mRowFields.end(); ++it) {
        if ((*it)->hasSkyscraper()) {
            sequence.emplace_back((*it)->skyscraper());
        }
        else {
            nopeFieldIt = it;
        }
    }
    assert(nopeFieldIt != mRowFields.end());
    assert(skyscraperCount() == static_cast<int>(sequence.size()));

    auto missingValue =
        missingNumberInSequence(sequence.begin(), sequence.end());

    assert(missingValue >= 0 && missingValue <= static_cast<int>(size()));
    insertSkyscraper(nopeFieldIt, missingValue);
}

void Row::addNopesToAllNopeFields(int nope)
{
    for (auto it = mRowFields.begin(); it != mRowFields.end(); ++it) {
        if ((*it)->hasSkyscraper()) {
            continue;
        }
        insertNope(it, nope);
    }
}

bool Row::allFieldsContainSkyscraper() const
{
    return skyscraperCount() == static_cast<int>(size());
}

int Row::skyscraperCount() const
{
    int count = 0;
    for (auto cit = mRowFields.cbegin(); cit != mRowFields.cend(); ++cit) {
        if ((*cit)->hasSkyscraper()) {
            ++count;
        }
    }
    return count;
}

int Row::nopeCount(int nope) const
{
    int count = 0;
    for (auto cit = mRowFields.cbegin(); cit != mRowFields.cend(); ++cit) {
        if ((*cit)->hasSkyscraper()) {
            continue;
        }
        if ((*cit)->containsNope(nope)) {
            ++count;
        }
    }
    return count;
}

void Row::guessSkyscraperOutOfNeighbourNopes()
{
    for (;;) {
        auto optNope = nopeValueInAllButOneField();
        if (!optNope) {
            break;
        }
        insertSkyscraperToFirstFieldWithoutNope(*optNope);
    }
}

bool Row::hasSkyscrapers(const std::vector<int> &skyscrapers,
                         Row::Direction direction) const
{
    if (direction == Direction::front) {
        return hasSkyscrapers(skyscrapers.cbegin(), skyscrapers.cend(),
                              mRowFields.cbegin(), mRowFields.cend());
    }
    return hasSkyscrapers(skyscrapers.cbegin(), skyscrapers.cend(),
                          mRowFields.crbegin(), mRowFields.crend());
}

bool Row::hasNopes(const std::vector<std::vector<int>> &nopes,
                   Direction direction) const
{
    if (direction == Direction::front) {
        return hasNopes(nopes.cbegin(), nopes.cend(), mRowFields.cbegin(),
                        mRowFields.cend());
    }
    return hasNopes(nopes.cbegin(), nopes.cend(), mRowFields.crbegin(),
                    mRowFields.crend());
}

void Row::addSkyscrapers(const std::vector<int> &skyscrapers,
                         Direction direction)
{
    if (direction == Direction::front) {
        addSkyscrapers(skyscrapers.begin(), skyscrapers.end(),
                       mRowFields.begin(), mRowFields.end());
    }
    else {
        addSkyscrapers(skyscrapers.begin(), skyscrapers.end(),
                       mRowFields.rbegin(), mRowFields.rend());
    }
}
void Row::addNopes(const std::vector<std::vector<int>> &nopes,
                   Direction direction)
{
    if (direction == Direction::front) {
        addNopes(nopes.begin(), nopes.end(), mRowFields.begin(),
                 mRowFields.end());
    }
    else {
        addNopes(nopes.begin(), nopes.end(), mRowFields.rbegin(),
                 mRowFields.rend());
    }
}

std::vector<Field *> Row::getFields() const
{
    return mRowFields;
}

template <typename SkyIterator, typename FieldIterator>
bool Row::hasSkyscrapers(SkyIterator skyItBegin, SkyIterator skyItEnd,
                         FieldIterator fieldItBegin,
                         FieldIterator fieldItEnd) const
{
    auto skyIt = skyItBegin;
    for (auto fieldIt = fieldItBegin;
         fieldIt != fieldItEnd && skyIt != skyItEnd; ++fieldIt, ++skyIt) {
        if (*skyIt == 0 && (*fieldIt)->hasSkyscraper()) {
            continue;
        }
        if ((*fieldIt)->skyscraper() != *skyIt) {
            return false;
        }
    }
    return true;
}

template <typename NopesIterator, typename FieldIterator>
bool Row::hasNopes(NopesIterator nopesItBegin, NopesIterator nopesItEnd,
                   FieldIterator fieldItBegin, FieldIterator fieldItEnd) const
{
    auto nopesIt = nopesItBegin;
    for (auto fieldIt = fieldItBegin;
         fieldIt != fieldItEnd && nopesIt != nopesItEnd; ++fieldIt, ++nopesIt) {

        if (nopesIt->empty()) {
            continue;
        }
        if ((*fieldIt)->hasSkyscraper()) {
            return false;
        }
        if (!(*fieldIt)->containsNopes(*nopesIt)) {
            return false;
        }
    }
    return true;
}

template <typename SkyIterator, typename FieldIterator>
void Row::addSkyscrapers(SkyIterator skyItBegin, SkyIterator skyItEnd,
                         FieldIterator fieldItBegin, FieldIterator fieldItEnd)
{
    auto skyIt = skyItBegin;
    for (auto fieldIt = fieldItBegin;
         fieldIt != fieldItEnd && skyIt != skyItEnd; ++fieldIt, ++skyIt) {
        if (*skyIt == 0) {
            continue;
        }
        insertSkyscraper(fieldIt, *skyIt);
    }
}

template <typename NopesIterator, typename FieldIterator>
void Row::addNopes(NopesIterator nopesItBegin, NopesIterator nopesItEnd,
                   FieldIterator fieldItBegin, FieldIterator fieldItEnd)
{
    auto nopesIt = nopesItBegin;
    for (auto fieldIt = fieldItBegin;
         fieldIt != fieldItEnd && nopesIt != nopesItEnd; ++fieldIt, ++nopesIt) {
        if (nopesIt->empty()) {
            continue;
        }
        insertNopes(fieldIt, *nopesIt);
    }
}

template <typename FieldIterator>
void Row::insertSkyscraper(FieldIterator fieldIt, int skyscraper)
{
    assert(mCrossingRows.size() == size());

    if ((*fieldIt)->hasSkyscraper()) {
        return;
    }
    (*fieldIt)->insertSkyscraper(skyscraper);

    if (hasOnlyOneNopeField()) {
        addLastMissingSkyscraper();
    }
    addNopesToAllNopeFields(skyscraper);

    int idx = getIdx(fieldIt);

    if (mCrossingRows(idx)->hasOnlyOneNopeField()) {
        mCrossingRows(idx)->addLastMissingSkyscraper();
    }

    mCrossingRows(idx)->addNopesToAllNopeFields(skyscraper);
}

template <typename FieldIterator>
void Row::insertNope(FieldIterator fieldIt, int nope)
{
    if ((*fieldIt)->hasSkyscraper()) {
        return;
    }
    if ((*fieldIt)->containsNope(nope)) {
        return;
    }

    bool hasSkyscraperBefore = (*fieldIt)->hasSkyscraper();
    (*fieldIt)->insertNope(nope);

    // skyscraper was added so we have to add nopes to the neighbours
    // probaly could insert only nopes directly
    if (!hasSkyscraperBefore && (*fieldIt)->hasSkyscraper()) {
        insertSkyscraper(fieldIt, (*fieldIt)->skyscraper());
    }

    if (onlyOneFieldWithoutNope(nope)) {
        insertSkyscraperToFirstFieldWithoutNope(nope);
    }

    int idx = getIdx(fieldIt);

    if (mCrossingRows(idx)->onlyOneFieldWithoutNope(nope)) {
        mCrossingRows(idx)->insertSkyscraperToFirstFieldWithoutNope(nope);
    }
}

template <typename IteratorType>
void Row::insertNopes(IteratorType it, const std::vector<int> &nopes)
{
    for (const auto &nope : nopes) {
        insertNope(it, nope);
    }
}

int Row::getIdx(std::vector<Field *>::const_iterator cit) const
{
    return std::distance(mRowFields.cbegin(), cit);
}

int Row::getIdx(std::vector<Field *>::const_reverse_iterator crit) const
{
    return size() - std::distance(mRowFields.crbegin(), crit) - 1;
}

std::vector<Field *> Row::getRowFields(const ReadDirection &readDirection,
                                       std::vector<Field> &boardFields,
                                       std::size_t size,
                                       const Point &startPoint)
{
    std::vector<Field *> fields;
    fields.reserve(size);
    std::size_t x = startPoint.x;
    std::size_t y = startPoint.y;

    if (readDirection == ReadDirection::topToBottom) {
        for (std::size_t i = 0; i < size; ++i) {
            fields.emplace_back(&boardFields(x + y * size));
            ++y;
        }
    }
    else { // ReadDirection::rightToLeft
        for (std::size_t i = 0; i < size; ++i) {
            fields.emplace_back(&boardFields(x + y * size));
            --x;
        }
    }
    return fields;
}

bool Row::onlyOneFieldWithoutNope(int nope) const
{
    if (nopeExistsAsSkyscraperInFields(mRowFields, nope)) {
        return false;
    }
    if (nopeCount(nope) < static_cast<int>(size()) - skyscraperCount() - 1) {
        return false;
    }
    return true;
}

bool Row::nopeExistsAsSkyscraperInFields(const std::vector<Field *> &rowFields,
                                         int nope) const
{
    auto cit = std::find_if(
        rowFields.cbegin(), rowFields.cend(),
        (nope)(const auto &field) { return field->skyscraper() == nope; });
    return cit != rowFields.cend();
}

std::optional<int> Row::nopeValueInAllButOneField() const
{
    std::unordered_map<int, int> nopeAndCount;

    for (auto cit = mRowFields.cbegin(); cit != mRowFields.cend(); ++cit) {
        if (!(*cit)->hasSkyscraper()) {
            auto nopes = (*cit)->nopes();
            for (const auto &nope : nopes) {
                if (hasSkyscraper(nope)) {
                    continue;
                }
                ++nopeAndCount(nope);
            }
        }
    }
    for (auto cit = nopeAndCount.cbegin(); cit != nopeAndCount.end(); ++cit) {
        if (cit->second == static_cast<int>(size()) - skyscraperCount() - 1) {
            return {cit->first};
        }
    }
    return {};
}

void Row::insertSkyscraperToFirstFieldWithoutNope(int nope)
{
    for (auto it = mRowFields.begin(); it != mRowFields.end(); ++it) {
        if ((*it)->hasSkyscraper()) {
            continue;
        }
        if (!(*it)->containsNope(nope)) {
            insertSkyscraper(it, nope);
            return; // there can be max one skyscraper per row;
        }
    }
}

bool Row::hasSkyscraper(int skyscraper) const
{
    for (const auto &field : mRowFields) {
        if (field->skyscraper() == skyscraper) {
            return true;
        }
    }
    return false;
}

class BorderIterator {
public:
    BorderIterator(std::size_t boardSize);

    Point point() const;
    ReadDirection readDirection() const;

    BorderIterator &operator++();

private:
    int mIdx = 0;
    std::size_t mBoardSize;
    Point mPoint{0, 0};
    ReadDirection mReadDirection{ReadDirection::topToBottom};
};

BorderIterator::BorderIterator(std::size_t boardSize) : mBoardSize{boardSize}
{
}

Point BorderIterator::point() const
{
    return mPoint;
}

ReadDirection BorderIterator::readDirection() const
{
    return mReadDirection;
}

BorderIterator &BorderIterator::operator++()
{
    ++mIdx;
    if (mIdx == static_cast<int>(2 * mBoardSize)) {
        return *this;
    }
    if (mIdx != 0 && mIdx % mBoardSize == 0) {
        nextDirection(mReadDirection);
    }

    advanceToNextPosition(mPoint, mReadDirection, mIdx % mBoardSize);
    return *this;
}

struct Board {
    Board(std::size_t size);

    void insert(const std::vector<std::optional<ClueHints>> &clueHints);

    void insert(const std::vector<std::vector<int>> &startingSkyscrapers);

    bool isSolved() const;

    std::vector<Field> fields;

    std::vector<Row> mRows;

    std::vector<std::vector<int>> skyscrapers2d() const;

    std::size_t size() const;

private:
    void makeRows();
    void connnectRowsWithCrossingRows();

    std::size_t mSize;
};

Board::Board(std::size_t size)
    : fields{std::vector<Field>(size * size, Field{size})}, mSize{size}
{
    makeRows();
}

void Board::insert(const std::vector<std::optional<ClueHints>> &clueHints)
{
    assert(clueHints.size() == mRows.size());

    for (std::size_t i = 0; i < clueHints.size(); ++i) {
        if (!clueHints(i)) {
            continue;
        }
        mRows(i).addNopes(clueHints(i)->nopes, Row::Direction::front);
        mRows(i).addSkyscrapers(clueHints(i)->skyscrapers,
                                Row::Direction::front);
    }
}

void Board::insert(const std::vector<std::vector<int>> &startingSkyscrapers)
{
    if (startingSkyscrapers.empty()) {
        return;
    }
    std::size_t boardSize = mRows.size() / 2;
    assert(startingSkyscrapers.size() == boardSize);

    for (std::size_t i = 0; i < startingSkyscrapers.size(); ++i) {
        mRows(i + boardSize).addSkyscrapers(startingSkyscrapers(i),
                                            Row::Direction::back);
    }
}

bool Board::isSolved() const
{
    std::size_t endVerticalRows = mRows.size() / 2;
    for (std::size_t i = 0; i < endVerticalRows; ++i) {
        if (!mRows(i).allFieldsContainSkyscraper()) {
            return false;
        }
    }
    return true;
}

std::vector<std::vector<int>> Board::skyscrapers2d() const
{
    std::vector<std::vector<int>> skyscrapers2d(mSize, std::vector<int>());

    std::size_t j = 0;
    skyscrapers2d(j).reserve(mSize);
    for (std::size_t i = 0; i < fields.size(); ++i) {
        if (i != 0 && i % mSize == 0) {
            ++j;
            skyscrapers2d(j).reserve(mSize);
        }
        skyscrapers2d(j).emplace_back(fields(i).skyscraper());
    }
    return skyscrapers2d;
}

std::size_t Board::size() const
{
    return mSize;
}

void Board::makeRows()
{
    BorderIterator borderIterator{mSize};

    std::size_t rowSize = mSize * 2;
    mRows.reserve(rowSize);

    for (std::size_t i = 0; i < rowSize; ++i, ++borderIterator) {
        mRows.emplace_back(Row{fields, mSize, borderIterator.point(),
                               borderIterator.readDirection()});
    }
    connnectRowsWithCrossingRows();
}

void Board::connnectRowsWithCrossingRows()
{
    std::size_t boardSize = mRows.size() / 2;

    std::vector<int> targetRowsIdx(boardSize);
    std::iota(targetRowsIdx.begin(), targetRowsIdx.end(), boardSize);

    for (std::size_t i = 0; i < mRows.size(); ++i) {
        if (i == mRows.size() / 2) {
            std::iota(targetRowsIdx.begin(), targetRowsIdx.end(), 0);
            std::reverse(targetRowsIdx.begin(), targetRowsIdx.end());
        }

        for (const auto &targetRowIdx : targetRowsIdx) {
            mRows(i).addCrossingRows(&mRows(targetRowIdx));
        }
    }
}

void debug_print(Board &board, const std::string &title)
{
    std::cout << title << 'n';

    for (std::size_t i = 0; i < board.fields.size(); ++i) {

        if (i % board.size() == 0 && i != 0) {
            std::cout << 'n';
        }

        if (board.fields(i).skyscraper() != 0) {
            std::cout << std::setw(board.size() * 2);
            std::cout << "V" + std::to_string(board.fields(i).skyscraper());
        }
        else if (board.fields(i).skyscraper() == 0 &&
                 !board.fields(i).nopes().empty()) {
            auto nopes_set = board.fields(i).nopes();
            std::vector<int> nopes(nopes_set.begin(), nopes_set.end());
            std::sort(nopes.begin(), nopes.end());

            std::string nopesStr;
            for (std::size_t i = 0; i < nopes.size(); ++i) {
                nopesStr.append(std::to_string(nopes(i)));
                if (i != nopes.size() - 1) {
                    nopesStr.push_back(',');
                }
            }
            std::cout << std::setw(board.size() * 2);
            std::cout << nopesStr;
        }
        else {
            std::cout << ' ';
        }
    }
    std::cout << 'n';
}

template <typename FieldIterator>
int visibleBuildings(FieldIterator begin, FieldIterator end)
{
    int visibleBuildingsCount = 0;
    int highestSeen = 0;
    for (auto it = begin; it != end; ++it) {
        if (it->skyscraper() != 0 && it->skyscraper() > highestSeen) {
            ++visibleBuildingsCount;
            highestSeen = it->skyscraper();
        }
    }
    return visibleBuildingsCount;
}

bool rowsAreValid(const std::vector<Field> &fields, std::size_t index,
                  std::size_t rowSize)
{
    std::size_t row = index / rowSize;
    for (std::size_t currIndex = row * rowSize; currIndex < (row + 1) * rowSize;
         ++currIndex) {
        if (currIndex == index) {
            continue;
        }
        if (fields(currIndex).skyscraper() == fields(index).skyscraper()) {
            return false;
        }
    }
    return true;
}

bool columnsAreValid(const std::vector<Field> &fields, std::size_t index,
                     std::size_t rowSize)
{
    std::size_t column = index % rowSize;

    for (std::size_t i = 0; i < rowSize; ++i) {
        std::size_t currIndex = column + i * rowSize;
        if (currIndex == index) {
            continue;
        }
        if (fields(currIndex).skyscraper() == fields(index).skyscraper()) {
            return false;
        }
    }
    return true;
}

std::tuple<int, int> getRowClues(const std::vector<int> &clues, std::size_t row,
                                 std::size_t rowSize)
{
    int frontClue = clues(clues.size() - 1 - row);
    int backClue = clues(rowSize + row);
    return {frontClue, backClue};
}

bool rowCluesAreValid(const std::vector<Field> &fields,
                      const std::vector<int> &clues, std::size_t index,
                      std::size_t rowSize)
{
    std::size_t row = index / rowSize;

    auto (frontClue, backClue) = getRowClues(clues, row, rowSize);

    if (frontClue == 0 && backClue == 0) {
        return true;
    }

    std::size_t rowIndexBegin = row * rowSize;
    std::size_t rowIndexEnd = (row + 1) * rowSize;

    auto citBegin = fields.cbegin() + rowIndexBegin;
    auto citEnd = fields.cbegin() + rowIndexEnd;

    bool rowIsFull = std::find_if(citBegin, citEnd, ()(const Field &field) {
                         return !field.hasSkyscraper();
                     }) == citEnd;

    if (!rowIsFull) {
        return true;
    }

    if (frontClue != 0) {
        auto frontVisible = visibleBuildings(citBegin, citEnd);

        if (frontClue != frontVisible) {
            return false;
        }
    }

    auto critBegin = std::make_reverse_iterator(citEnd);
    auto critEnd = std::make_reverse_iterator(citBegin);

    if (backClue != 0) {
        auto backVisible = visibleBuildings(critBegin, critEnd);

        if (backClue != backVisible) {
            return false;
        }
    }
    return true;
}

std::tuple<int, int> getColumnClues(const std::vector<int> &clues,
                                    std::size_t x, std::size_t size)
{
    int frontClue = clues(x);
    int backClue = clues(size * 3 - 1 - x);
    return {frontClue, backClue};
}

bool columnCluesAreValid(const std::vector<Field> &fields,
                         const std::vector<int> &clues, std::size_t index,
                         std::size_t rowSize)
{
    std::size_t column = index % rowSize;

    auto (frontClue, backClue) = getColumnClues(clues, column, rowSize);

    if (frontClue == 0 && backClue == 0) {
        return true;
    }

    std::vector<Field> verticalFields;
    verticalFields.reserve(rowSize);

    for (std::size_t i = 0; i < rowSize; ++i) {
        verticalFields.emplace_back(fields(column + i * rowSize));
    }

    bool columnIsFull =
        std::find_if(verticalFields.cbegin(), verticalFields.cend(),
                     ()(const Field &field) {
                         return !field.hasSkyscraper();
                     }) == verticalFields.cend();

    if (!columnIsFull) {
        return true;
    }

    if (frontClue != 0) {
        auto frontVisible =
            visibleBuildings(verticalFields.cbegin(), verticalFields.cend());
        if (frontClue != frontVisible) {
            return false;
        }
    }
    if (backClue != 0) {
        auto backVisible =
            visibleBuildings(verticalFields.crbegin(), verticalFields.crend());

        if (backClue != backVisible) {
            return false;
        }
    }
    return true;
}

bool skyscrapersAreValidPositioned(const std::vector<Field> &fields,
                                   const std::vector<int> &clues,
                                   std::size_t index, std::size_t rowSize)
{
    if (!rowsAreValid(fields, index, rowSize)) {
        return false;
    }
    if (!columnsAreValid(fields, index, rowSize)) {
        return false;
    }
    if (!rowCluesAreValid(fields, clues, index, rowSize)) {
        return false;
    }
    if (!columnCluesAreValid(fields, clues, index, rowSize)) {
        return false;
    }
    return true;
}

bool guessSkyscrapers(Board &board, const std::vector<int> &clues,
                      std::size_t index, std::size_t countOfElements,
                      std::size_t rowSize)
{
    if (index == countOfElements) {
        return true;
    }

    if (board.fields(index).skyscraper() != 0) {
        if (!skyscrapersAreValidPositioned(board.fields, clues, index,
                                           rowSize)) {
            return false;
        }
        if (guessSkyscrapers(board, clues, index + 1, countOfElements,
                             rowSize)) {
            return true;
        }
        return false;
    }

    auto oldBitmask = board.fields(index).bitmask();
    for (int trySkyscraper = 1; trySkyscraper <= static_cast<int>(rowSize);
         ++trySkyscraper) {

        if (board.fields(index).containsNope(trySkyscraper)) {
            continue;
        }
        board.fields(index).insertSkyscraper(trySkyscraper);
        if (!skyscrapersAreValidPositioned(board.fields, clues, index,
                                           rowSize)) {
            board.fields(index).setBitmask(oldBitmask);
            continue;
        }
        if (guessSkyscrapers(board, clues, index + 1, countOfElements,
                             rowSize)) {
            return true;
        }
        board.fields(index).setBitmask(oldBitmask);
    }
    board.fields(index).setBitmask(oldBitmask);
    return false;
}

std::vector<std::vector<int>>
SolvePuzzle(const std::vector<int> &clues,
            std::vector<std::vector<int>> startingGrid, int)
{
    assert(clues.size() % 4 == 0);

    std::size_t boardSize = clues.size() / 4;

    auto clueHints = getClueHints(clues, boardSize);

    Board board{boardSize};

    board.insert(clueHints);
    board.insert(startingGrid);

    if (board.isSolved()) {
        return board.skyscrapers2d();
    }
    guessSkyscrapers(board, clues, 0, board.fields.size(), board.size());

    return board.skyscrapers2d();
}

std::vector<std::vector<int>> SolvePuzzle(const std::vector<int> &clues)
{
    return SolvePuzzle(clues, std::vector<std::vector<int>>{}, 0);
}

} // namespace codewarsbacktracking

Additional things like the unit test can be found here

c++ – Skyscraper Solver for NxN Size Version 2 (Using Backtracking)

This is a follow up of Skyscraper Solver for NxN Size

I followed the advice in the last question and changed my approach from generating Permutations to use Backtracking to solve the puzzle.

Unfortunately my solution is still to slow for certain tests.

Here are the worst test cases:

struct TestDataProvider {
    std::vector<int> clues;
    std::vector<std::vector<int>> result;
    std::vector<std::vector<int>> board;
};

TestDataProvider sky7_random{{0, 5, 0, 5, 0, 2, 0, 0, 0, 0, 4, 0, 0, 3,
                              6, 4, 0, 2, 0, 0, 3, 0, 3, 3, 3, 0, 0, 4},
                             {{{2, 3, 6, 1, 4, 5, 7},
                               {7, 1, 5, 2, 3, 4, 6},
                               {6, 4, 2, 3, 1, 7, 5},
                               {4, 5, 7, 6, 2, 3, 1},
                               {3, 2, 1, 5, 7, 6, 4},
                               {1, 6, 4, 7, 5, 2, 3},
                               {5, 7, 3, 4, 6, 1, 2}}}};
                                  
                                 

TestDataProvider sky11_medium_partial_2{
    {1, 2, 2, 5, 3, 2, 5, 3, 5, 4, 3, 4, 2, 3, 1, 2, 3, 2, 4, 3, 4, 4,
     3, 4, 3, 2, 3, 5, 3, 1, 2, 3, 3, 3, 3, 2, 3, 5, 2, 5, 3, 4, 2, 1},
    {{11, 9, 10, 5, 8, 6, 2, 4, 1, 3, 7},
     {9, 1, 3, 8, 7, 11, 6, 5, 2, 4, 10},
     {6, 2, 1, 7, 3, 9, 8, 11, 5, 10, 4},
     {7, 6, 4, 2, 10, 8, 1, 3, 9, 5, 11},
     {2, 7, 6, 9, 4, 10, 3, 8, 11, 1, 5},
     {4, 11, 2, 6, 9, 5, 10, 1, 3, 7, 8},
     {1, 4, 7, 10, 2, 3, 5, 6, 8, 11, 9},
     {3, 8, 5, 4, 11, 7, 9, 2, 10, 6, 1},
     {10, 5, 8, 1, 6, 2, 11, 7, 4, 9, 3},
     {5, 10, 11, 3, 1, 4, 7, 9, 6, 8, 2},
     {8, 3, 9, 11, 5, 1, 4, 10, 7, 2, 6}},
    {{0, 0, 10, 0, 8, 0, 2, 0, 1, 0, 0},
     {0, 0, 0, 0, 7, 11, 0, 5, 0, 0, 0},
     {0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 4},
     {0, 0, 0, 0, 0, 0, 1, 3, 9, 0, 0},
     {0, 0, 6, 0, 4, 0, 3, 0, 11, 1, 0},
     {0, 0, 0, 6, 9, 0, 0, 1, 3, 0, 8},
     {0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 9},
     {0, 0, 0, 4, 11, 0, 0, 0, 0, 0, 1},
     {10, 0, 8, 1, 0, 2, 11, 7, 4, 0, 0},
     {5, 10, 0, 0, 0, 0, 0, 0, 0, 8, 0},
     {0, 0, 9, 0, 5, 0, 0, 0, 7, 0, 6}}};

     
TEST(CodewarsBacktracking, sky7_random)
{
    EXPECT_EQ(codewarsbacktracking::SolvePuzzle(sky7_random.clues),
              sky7_random.result);
}


TEST(CodewarsBacktracking, sky11_medium_partial_2)
{
    EXPECT_EQ(codewarsbacktracking::SolvePuzzle(
                  sky11_medium_partial_2.clues, sky11_medium_partial_2.board,
                  sky11_medium_partial_2.board.size()),
              sky11_medium_partial_2.result);
}

The first test tries to solve a 7×7 Board with no skyscrapers on the grid. A few Skyscrapers are computed out of the clues provided but then my backtracking routine needs to insert ~440 mio times until the puzzle is solved.

The other test case is a 11×11 board which is already partially filled but also has not many clues.

On my system the run times look like this in release mode (Including other unit tests which are a lot faster):

(----------) 35 tests from CodewarsBacktracking
( RUN      ) CodewarsBacktracking.sky4_easy
(       OK ) CodewarsBacktracking.sky4_easy (0 ms)
( RUN      ) CodewarsBacktracking.sky4_easy_2
(       OK ) CodewarsBacktracking.sky4_easy_2 (0 ms)
( RUN      ) CodewarsBacktracking.sky4_hard
(       OK ) CodewarsBacktracking.sky4_hard (0 ms)
( RUN      ) CodewarsBacktracking.sky4_hard_2
(       OK ) CodewarsBacktracking.sky4_hard_2 (0 ms)
( RUN      ) CodewarsBacktracking.sky6_easy
(       OK ) CodewarsBacktracking.sky6_easy (5 ms)
( RUN      ) CodewarsBacktracking.sky6_medium
(       OK ) CodewarsBacktracking.sky6_medium (3 ms)
( RUN      ) CodewarsBacktracking.sky6_hard
(       OK ) CodewarsBacktracking.sky6_hard (5 ms)
( RUN      ) CodewarsBacktracking.sky6_hard_2
(       OK ) CodewarsBacktracking.sky6_hard_2 (1 ms)
( RUN      ) CodewarsBacktracking.sky6_random
(       OK ) CodewarsBacktracking.sky6_random (13 ms)
( RUN      ) CodewarsBacktracking.sky6_random_2
(       OK ) CodewarsBacktracking.sky6_random_2 (1 ms)
( RUN      ) CodewarsBacktracking.sky6_random_3
(       OK ) CodewarsBacktracking.sky6_random_3 (3 ms)
( RUN      ) CodewarsBacktracking.sky7_medium
(       OK ) CodewarsBacktracking.sky7_medium (27 ms)
( RUN      ) CodewarsBacktracking.sky7_hard
(       OK ) CodewarsBacktracking.sky7_hard (54 ms)
( RUN      ) CodewarsBacktracking.sky7_very_hard
(       OK ) CodewarsBacktracking.sky7_very_hard (336 ms)
    ( RUN      ) CodewarsBacktracking.sky7_random
    (       OK ) CodewarsBacktracking.sky7_random (72952 ms)
( RUN      ) CodewarsBacktracking.sky4_partial
(       OK ) CodewarsBacktracking.sky4_partial (0 ms)
( RUN      ) CodewarsBacktracking.sky4_partial_2
(       OK ) CodewarsBacktracking.sky4_partial_2 (0 ms)
( RUN      ) CodewarsBacktracking.sky5_partial
(       OK ) CodewarsBacktracking.sky5_partial (0 ms)
( RUN      ) CodewarsBacktracking.sky5_partial_2
(       OK ) CodewarsBacktracking.sky5_partial_2 (0 ms)
( RUN      ) CodewarsBacktracking.sky6_partial
(       OK ) CodewarsBacktracking.sky6_partial (0 ms)
( RUN      ) CodewarsBacktracking.sky6_partial_2
(       OK ) CodewarsBacktracking.sky6_partial_2 (1 ms)
( RUN      ) CodewarsBacktracking.sky7_easy_partial
(       OK ) CodewarsBacktracking.sky7_easy_partial (0 ms)
( RUN      ) CodewarsBacktracking.sky7_easy_partial_2
(       OK ) CodewarsBacktracking.sky7_easy_partial_2 (0 ms)
( RUN      ) CodewarsBacktracking.sky7_medium_partial
(       OK ) CodewarsBacktracking.sky7_medium_partial (1 ms)
( RUN      ) CodewarsBacktracking.sky7_hard_partial
(       OK ) CodewarsBacktracking.sky7_hard_partial (42 ms)
( RUN      ) CodewarsBacktracking.sky8_easy_partial
(       OK ) CodewarsBacktracking.sky8_easy_partial (1 ms)
( RUN      ) CodewarsBacktracking.sky8_medium_partial
(       OK ) CodewarsBacktracking.sky8_medium_partial (1 ms)
( RUN      ) CodewarsBacktracking.sky8_hard_partial
(       OK ) CodewarsBacktracking.sky8_hard_partial (1390 ms)
( RUN      ) CodewarsBacktracking.sky9_easy_partial
(       OK ) CodewarsBacktracking.sky9_easy_partial (1 ms)
( RUN      ) CodewarsBacktracking.sky9_easy_partial_2
(       OK ) CodewarsBacktracking.sky9_easy_partial_2 (1 ms)
( RUN      ) CodewarsBacktracking.sky10_easy_partial
(       OK ) CodewarsBacktracking.sky10_easy_partial (1 ms)
( RUN      ) CodewarsBacktracking.sky10_easy_partial_2
(       OK ) CodewarsBacktracking.sky10_easy_partial_2 (0 ms)
( RUN      ) CodewarsBacktracking.sky11_easy_partial
(       OK ) CodewarsBacktracking.sky11_easy_partial (2 ms)
( RUN      ) CodewarsBacktracking.sky11_medium_partial
(       OK ) CodewarsBacktracking.sky11_medium_partial (661 ms)
    ( RUN      ) CodewarsBacktracking.sky11_medium_partial_2
    (       OK ) CodewarsBacktracking.sky11_medium_partial_2 (36910 ms)
(----------) 35 tests from CodewarsBacktracking (112413 ms total)

So I wonder is there any way to make the backtracking faster here or is there just no faster way to solve skyscraper puzzles? Is it possible to reduce the calls to the backtracking routine ?

I already try to abort the insertion as soon as the board is getting invalid. I cannot come up with another tweak to reduce the backtracking calls.

Below the full code for the backtracking.

The interesting part which does the backtracking is the function guessSkyscrapers:

bool guessSkyscrapers(Board &board, const std::vector<int> &clues,
                      std::size_t x, std::size_t y, std::size_t size)
{
    if (x == size) {
        x = 0;
        y++;
    };
    if (y == size) {
        return true;
    }
    if (board.skyscrapers(y)(x) != 0) {
        if (!skyscrapersAreValidPositioned(board.skyscrapers, clues, x, y,
                                           size)) {
            return false;
        }
        if (guessSkyscrapers(board, clues, x + 1, y, size)) {
            return true;
        }
        else {
            return false;
        }
    }

    for (int trySkyscraper = 1;
         trySkyscraper <= static_cast<int>(board.skyscrapers.size());
         ++trySkyscraper) {

        if (board.nopes(y)(x).contains(trySkyscraper)) {
            continue;
        }
        board.skyscrapers(y)(x) = trySkyscraper;
        if (!skyscrapersAreValidPositioned(board.skyscrapers, clues, x, y,
                                           size)) {
            continue;
        }
        if (guessSkyscrapers(board, clues, x + 1, y, size)) {
            return true;
        }
    }
    board.skyscrapers(y)(x) = 0;
    return false;
}

Let me know if you have any idea how to speed up solving this puzzles.

If you want to see the whole github project which contains:

-Backtracking Solution in one file (like here requirement for codewars)
-Backtracking Solution in multiple files
-Permutations Solution in one file (like here requirement for codewars)
-Permutations Solution in multiple files
-Full Unit test suite

You can find it here

Full backtracking code:

codewarsbacktracking.cpp

#include <algorithm>
#include <cassert>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <string>
#include <unordered_set>

namespace codewarsbacktracking {

struct ClueHints {
    ClueHints(std::size_t boardSize);
    ClueHints();

    void reverse();

    void removeNopesOnSkyscrapers();

    std::vector<int> skyscrapers{};
    std::vector<std::vector<int>> nopes{};
};

ClueHints::ClueHints(std::size_t boardSize)
    : skyscrapers{std::vector<int>(boardSize, 0)},
      nopes{std::vector<std::vector<int>>(boardSize, std::vector<int>{})}
{
}

void ClueHints::reverse()
{
    std::reverse(skyscrapers.begin(), skyscrapers.end());
    std::reverse(nopes.begin(), nopes.end());
}

void ClueHints::removeNopesOnSkyscrapers()
{
    for (std::size_t i = 0; i < skyscrapers.size(); ++i) {
        if (skyscrapers(i) == 0) {
            continue;
        }
        nopes(i).clear();
    }
}

std::optional<ClueHints> getClueHints(int clue, std::size_t boardSize)
{
    if (clue == 0) {
        return {};
    }

    ClueHints clueHints{boardSize};

    std::vector<std::unordered_set<int>> nopes(boardSize,
                                               std::unordered_set<int>{});

    if (clue == static_cast<int>(boardSize)) {
        for (std::size_t i = 0; i < boardSize; ++i) {
            clueHints.skyscrapers(i) = i + 1;
        }
    }
    else if (clue == 1) {
        clueHints.skyscrapers(0) = boardSize;
    }
    else if (clue == 2) {
        nopes(0).insert(boardSize);
        nopes(1).insert(boardSize - 1);
    }
    else {
        for (std::size_t fieldIdx = 0;
             fieldIdx < static_cast<std::size_t>(clue - 1); ++fieldIdx) {

            for (std::size_t nopeValue = boardSize;
                 nopeValue >= (boardSize - (clue - 2) + fieldIdx);
                 --nopeValue) {
                nopes(fieldIdx).insert(nopeValue);
            }
        }
    }

    assert(nopes.size() == clueHints.nopes.size());

    for (std::size_t i = 0; i < nopes.size(); ++i) {
        clueHints.nopes(i) = std::vector<int>(nopes(i).begin(), nopes(i).end());
    }
    return {clueHints};
}

std::optional<ClueHints> merge(std::optional<ClueHints> optFrontClueHints,
                               std::optional<ClueHints> optBackClueHints)
{
    if (!optFrontClueHints && !optBackClueHints) {
        return {};
    }
    if (!optFrontClueHints) {
        optBackClueHints->reverse();
        return optBackClueHints;
    }
    if (!optBackClueHints) {
        return optFrontClueHints;
    }

    auto size = optFrontClueHints->skyscrapers.size();
    ClueHints clueHints{size};

    assert(optFrontClueHints->skyscrapers.size() ==
           optFrontClueHints->nopes.size());
    assert(optBackClueHints->skyscrapers.size() ==
           optBackClueHints->nopes.size());
    assert(optFrontClueHints->skyscrapers.size() ==
           optBackClueHints->skyscrapers.size());

    optBackClueHints->reverse();

    for (std::size_t i = 0; i < optFrontClueHints->skyscrapers.size(); ++i) {

        auto frontSkyscraper = optFrontClueHints->skyscrapers(i);
        auto backSkyscraper = optBackClueHints->skyscrapers(i);

        if (frontSkyscraper != 0 && backSkyscraper != 0) {
            assert(frontSkyscraper == backSkyscraper);
            clueHints.skyscrapers(i) = frontSkyscraper;
        }
        else if (frontSkyscraper != 0) {
            clueHints.skyscrapers(i) = frontSkyscraper;
            clueHints.nopes(i).clear();
        }
        else { // backSkyscraper != 0
            clueHints.skyscrapers(i) = backSkyscraper;
            clueHints.nopes(i).clear();
        }

        if (clueHints.skyscrapers(i) != 0) {
            continue;
        }

        std::unordered_set<int> nopes(optFrontClueHints->nopes(i).begin(),
                                      optFrontClueHints->nopes(i).end());
        nopes.insert(optBackClueHints->nopes(i).begin(),
                     optBackClueHints->nopes(i).end());
        clueHints.nopes(i) = std::vector<int>(nopes.begin(), nopes.end());
    }
    clueHints.removeNopesOnSkyscrapers();
    return {clueHints};
}

void mergeClueHintsPerRow(std::vector<std::optional<ClueHints>> &clueHints)
{
    std::size_t startOffset = clueHints.size() / 4 * 3 - 1;
    std::size_t offset = startOffset;

    for (std::size_t frontIdx = 0; frontIdx < clueHints.size() / 2;
         ++frontIdx, offset -= 2) {

        if (frontIdx == clueHints.size() / 4) {
            offset = startOffset;
        }

        int backIdx = frontIdx + offset;

        clueHints(frontIdx) = merge(clueHints(frontIdx), clueHints(backIdx));
    }
    clueHints.erase(clueHints.begin() + clueHints.size() / 2, clueHints.end());
}

std::vector<std::optional<ClueHints>>
getClueHints(const std::vector<int> &clues, std::size_t boardSize)
{
    std::vector<std::optional<ClueHints>> clueHints;
    clueHints.reserve(clues.size());

    for (const auto &clue : clues) {
        clueHints.emplace_back(getClueHints(clue, boardSize));
    }
    mergeClueHintsPerRow(clueHints);
    return clueHints;
}

template <typename It> int missingNumberInSequence(It begin, It end)
{
    int n = std::distance(begin, end) + 1;
    double projectedSum = (n + 1) * (n / 2.0);
    int actualSum = std::accumulate(begin, end, 0);
    return projectedSum - actualSum;
}

class Nopes {
public:
    Nopes(int size);

    void insert(int value);
    void insert(const std::vector<int> &values);
    bool sizeReached() const;
    int missingNumberInSequence() const;

    bool contains(int value) const;
    bool contains(const std::vector<int> &values);

    bool isEmpty() const;
    void clear();

    std::vector<int> containing() const;

    // for debug print
    std::unordered_set<int> values() const;

private:
    int mSize;
    std::unordered_set<int> mValues;
};

Nopes::Nopes(int size) : mSize{size}
{
    assert(size > 0);
}

void Nopes::insert(int value)
{
    assert(value >= 1 && value <= mSize + 1);
    mValues.insert(value);
}

void Nopes::insert(const std::vector<int> &values)
{
    mValues.insert(values.begin(), values.end());
}

bool Nopes::sizeReached() const
{
    return mValues.size() == static_cast<std::size_t>(mSize);
}

int Nopes::missingNumberInSequence() const
{
    assert(sizeReached());
    return codewarsbacktracking::missingNumberInSequence(mValues.begin(),
                                                         mValues.end());
}

bool Nopes::contains(int value) const
{
    auto it = mValues.find(value);
    return it != mValues.end();
}

bool Nopes::contains(const std::vector<int> &values)
{
    for (const auto &value : values) {
        if (!contains(value)) {
            return false;
        }
    }
    return true;
}

bool Nopes::isEmpty() const
{
    return mValues.empty();
}

void Nopes::clear()
{
    mValues.clear();
}

std::vector<int> Nopes::containing() const
{
    std::vector<int> nopes;
    nopes.reserve(mValues.size());
    for (const auto &value : mValues) {
        nopes.emplace_back(value);
    }
    return nopes;
}

std::unordered_set<int> Nopes::values() const
{
    return mValues;
}

class Field {
public:
    Field(int &skyscraper, Nopes &nopes);

    void insertSkyscraper(int skyscraper);
    void insertNope(int nope);
    void insertNopes(const std::vector<int> &nopes);

    bool fullOfNopes() const;

    int skyscraper() const;
    Nopes nopes() const;

    bool hasSkyscraper() const;

    std::optional<int> lastMissingNope() const;

private:
    int &mSkyscraper;
    Nopes &mNopes;
    bool mHasSkyscraper = false;
};

Field::Field(int &skyscraper, Nopes &nopes)
    : mSkyscraper{skyscraper}, mNopes{nopes}
{
}

void Field::insertSkyscraper(int skyscraper)
{
    assert(mSkyscraper == 0 || skyscraper == mSkyscraper);
    if (mHasSkyscraper) {
        return;
    }
    mSkyscraper = skyscraper;
    mHasSkyscraper = true;

    mNopes.clear();
}
void Field::insertNope(int nope)
{
    if (mHasSkyscraper) {
        return;
    }
    mNopes.insert(nope);
}
void Field::insertNopes(const std::vector<int> &nopes)
{
    if (mHasSkyscraper) {
        return;
    }
    mNopes.insert(nopes);
}

bool Field::fullOfNopes() const
{
    return mNopes.sizeReached();
}

int Field::skyscraper() const
{
    return mSkyscraper;
}
Nopes Field::nopes() const
{
    return mNopes;
}

bool Field::hasSkyscraper() const
{
    return mHasSkyscraper;
}

std::optional<int> Field::lastMissingNope() const
{
    if (!mNopes.sizeReached()) {
        return {};
    }
    return mNopes.missingNumberInSequence();
}

struct Point {
    int x;
    int y;
};

inline bool operator==(const Point &lhs, const Point &rhs)
{
    return lhs.x == rhs.x && lhs.y == rhs.y;
}
inline bool operator!=(const Point &lhs, const Point &rhs)
{
    return !(lhs == rhs);
}

enum class ReadDirection { topToBottom, rightToLeft };

void nextDirection(ReadDirection &readDirection);

void advanceToNextPosition(Point &point, ReadDirection readDirection,
                           int clueIdx);

void nextDirection(ReadDirection &readDirection)
{
    assert(readDirection != ReadDirection::rightToLeft);
    int dir = static_cast<int>(readDirection);
    ++dir;
    readDirection = static_cast<ReadDirection>(dir);
}

void advanceToNextPosition(Point &point, ReadDirection readDirection,
                           int clueIdx)
{
    if (clueIdx == 0) {
        return;
    }
    switch (readDirection) {
    case ReadDirection::topToBottom:
        ++point.x;
        break;
    case ReadDirection::rightToLeft:
        ++point.y;
        break;
    }
}

class Row {
public:
    Row(std::vector<std::vector<Field>> &fields, const Point &startPoint,
        const ReadDirection &readDirection);

    void insertSkyscraper(int pos, int skyscraper);

    std::size_t size() const;

    void addCrossingRows(Row *crossingRow);

    bool hasOnlyOneNopeField() const;
    void addLastMissingSkyscraper();

    void addNopesToAllNopeFields(int nope);

    bool allFieldsContainSkyscraper() const;

    int skyscraperCount() const;
    int nopeCount(int nope) const;

    void guessSkyscraperOutOfNeighbourNopes();

    enum class Direction { front, back };

    bool hasSkyscrapers(const std::vector<int> &skyscrapers,
                        Direction direction) const;
    bool hasNopes(const std::vector<std::vector<int>> &nopes,
                  Direction direction) const;

    void addSkyscrapers(const std::vector<int> &skyscrapers,
                        Direction direction);
    void addNopes(const std::vector<std::vector<int>> &nopes,
                  Direction direction);

    std::vector<Field *> getFields() const;

private:
    template <typename SkyIterator, typename FieldIterator>
    bool hasSkyscrapers(SkyIterator skyItBegin, SkyIterator skyItEnd,
                        FieldIterator fieldItBegin,
                        FieldIterator fieldItEnd) const;

    template <typename NopesIterator, typename FieldIterator>
    bool hasNopes(NopesIterator nopesItBegin, NopesIterator nopesItEnd,
                  FieldIterator fieldItBegin, FieldIterator fieldItEnd) const;

    template <typename SkyIterator, typename FieldIterator>
    void addSkyscrapers(SkyIterator skyItBegin, SkyIterator skyItEnd,
                        FieldIterator fieldItBegin, FieldIterator fieldItEnd);

    template <typename NopesIterator, typename FieldIterator>
    void addNopes(NopesIterator nopesItBegin, NopesIterator nopesItEnd,
                  FieldIterator fieldItBegin, FieldIterator fieldItEnd);

    template <typename IteratorType>
    void insertSkyscraper(IteratorType it, int skyscraper);

    template <typename IteratorType> void insertNope(IteratorType it, int nope);

    template <typename IteratorType>
    void insertNopes(IteratorType it, const std::vector<int> &nopes);

    int getIdx(std::vector<Field *>::const_iterator cit) const;
    int getIdx(std::vector<Field *>::const_reverse_iterator crit) const;

    std::vector<Field *> getRowFields(const ReadDirection &readDirection,
                                      std::vector<std::vector<Field>> &fields,
                                      const Point &startPoint);

    bool onlyOneFieldWithoutNope(int nope) const;
    std::optional<int> nopeValueInAllButOneField() const;

    void insertSkyscraperToFirstFieldWithoutNope(int nope);

    bool hasSkyscraper(int skyscraper) const;

    std::vector<Row *> mCrossingRows;
    std::vector<Field *> mRowFields;
};

Row::Row(std::vector<std::vector<Field>> &fields, const Point &startPoint,
         const ReadDirection &readDirection)
    : mRowFields{getRowFields(readDirection, fields, startPoint)}
{
}

void Row::insertSkyscraper(int pos, int skyscraper)
{
    assert(pos >= 0 && pos < static_cast<int>(mRowFields.size()));
    assert(skyscraper > 0 && skyscraper <= static_cast<int>(mRowFields.size()));
    auto it = mRowFields.begin() + pos;
    insertSkyscraper(it, skyscraper);
}

std::size_t Row::size() const
{
    return mRowFields.size();
}

void Row::addCrossingRows(Row *crossingRow)
{
    assert(crossingRow != nullptr);
    assert(mCrossingRows.size() < size());
    mCrossingRows.push_back(crossingRow);
}

bool Row::hasOnlyOneNopeField() const
{
    return skyscraperCount() == static_cast<int>(size() - 1);
}

void Row::addLastMissingSkyscraper()
{
    assert(hasOnlyOneNopeField());

    auto nopeFieldIt = mRowFields.end();
    std::vector<int> sequence;
    sequence.reserve(size() - 1);

    for (auto it = mRowFields.begin(); it != mRowFields.end(); ++it) {
        if ((*it)->hasSkyscraper()) {
            sequence.emplace_back((*it)->skyscraper());
        }
        else {
            nopeFieldIt = it;
        }
    }
    assert(nopeFieldIt != mRowFields.end());
    assert(skyscraperCount() == static_cast<int>(sequence.size()));
    auto missingValue =
        missingNumberInSequence(sequence.begin(), sequence.end());
    assert(missingValue >= 0 && missingValue <= static_cast<int>(size()));
    insertSkyscraper(nopeFieldIt, missingValue);
}

void Row::addNopesToAllNopeFields(int nope)
{
    for (auto it = mRowFields.begin(); it != mRowFields.end(); ++it) {
        if ((*it)->hasSkyscraper()) {
            continue;
        }
        insertNope(it, nope);
    }
}

bool Row::allFieldsContainSkyscraper() const
{
    return skyscraperCount() == static_cast<int>(size());
}

int Row::skyscraperCount() const
{
    int count = 0;
    for (auto cit = mRowFields.cbegin(); cit != mRowFields.cend(); ++cit) {
        if ((*cit)->hasSkyscraper()) {
            ++count;
        }
    }
    return count;
}

int Row::nopeCount(int nope) const
{
    int count = 0;
    for (auto cit = mRowFields.cbegin(); cit != mRowFields.cend(); ++cit) {
        if ((*cit)->nopes().contains(nope)) {
            ++count;
        }
    }
    return count;
}

void Row::guessSkyscraperOutOfNeighbourNopes()
{
    for (;;) {
        auto optNope = nopeValueInAllButOneField();
        if (!optNope) {
            break;
        }
        insertSkyscraperToFirstFieldWithoutNope(*optNope);
    }
}

bool Row::hasSkyscrapers(const std::vector<int> &skyscrapers,
                         Row::Direction direction) const
{
    if (direction == Direction::front) {
        return hasSkyscrapers(skyscrapers.cbegin(), skyscrapers.cend(),
                              mRowFields.cbegin(), mRowFields.cend());
    }
    return hasSkyscrapers(skyscrapers.cbegin(), skyscrapers.cend(),
                          mRowFields.crbegin(), mRowFields.crend());
}

bool Row::hasNopes(const std::vector<std::vector<int>> &nopes,
                   Direction direction) const
{
    if (direction == Direction::front) {
        return hasNopes(nopes.cbegin(), nopes.cend(), mRowFields.cbegin(),
                        mRowFields.cend());
    }
    return hasNopes(nopes.cbegin(), nopes.cend(), mRowFields.crbegin(),
                    mRowFields.crend());
}

void Row::addSkyscrapers(const std::vector<int> &skyscrapers,
                         Direction direction)
{
    if (direction == Direction::front) {
        addSkyscrapers(skyscrapers.begin(), skyscrapers.end(),
                       mRowFields.begin(), mRowFields.end());
    }
    else {
        addSkyscrapers(skyscrapers.begin(), skyscrapers.end(),
                       mRowFields.rbegin(), mRowFields.rend());
    }
}
void Row::addNopes(const std::vector<std::vector<int>> &nopes,
                   Direction direction)
{
    if (direction == Direction::front) {
        addNopes(nopes.begin(), nopes.end(), mRowFields.begin(),
                 mRowFields.end());
    }
    else {
        addNopes(nopes.begin(), nopes.end(), mRowFields.rbegin(),
                 mRowFields.rend());
    }
}

std::vector<Field *> Row::getFields() const
{
    return mRowFields;
}

template <typename SkyIterator, typename FieldIterator>
bool Row::hasSkyscrapers(SkyIterator skyItBegin, SkyIterator skyItEnd,
                         FieldIterator fieldItBegin,
                         FieldIterator fieldItEnd) const
{
    auto skyIt = skyItBegin;
    for (auto fieldIt = fieldItBegin;
         fieldIt != fieldItEnd && skyIt != skyItEnd; ++fieldIt, ++skyIt) {
        if (*skyIt == 0 && (*fieldIt)->hasSkyscraper()) {
            continue;
        }
        if ((*fieldIt)->skyscraper() != *skyIt) {
            return false;
        }
    }
    return true;
}

template <typename NopesIterator, typename FieldIterator>
bool Row::hasNopes(NopesIterator nopesItBegin, NopesIterator nopesItEnd,
                   FieldIterator fieldItBegin, FieldIterator fieldItEnd) const
{
    auto nopesIt = nopesItBegin;
    for (auto fieldIt = fieldItBegin;
         fieldIt != fieldItEnd && nopesIt != nopesItEnd; ++fieldIt, ++nopesIt) {

        if (nopesIt->empty()) {
            continue;
        }
        if ((*fieldIt)->hasSkyscraper()) {
            return false;
        }
        if (!(*fieldIt)->nopes().contains(*nopesIt)) {
            return false;
        }
    }
    return true;
}

template <typename SkyIterator, typename FieldIterator>
void Row::addSkyscrapers(SkyIterator skyItBegin, SkyIterator skyItEnd,
                         FieldIterator fieldItBegin, FieldIterator fieldItEnd)
{
    auto skyIt = skyItBegin;
    for (auto fieldIt = fieldItBegin;
         fieldIt != fieldItEnd && skyIt != skyItEnd; ++fieldIt, ++skyIt) {
        if (*skyIt == 0) {
            continue;
        }
        insertSkyscraper(fieldIt, *skyIt);
    }
}

template <typename NopesIterator, typename FieldIterator>
void Row::addNopes(NopesIterator nopesItBegin, NopesIterator nopesItEnd,
                   FieldIterator fieldItBegin, FieldIterator fieldItEnd)
{
    auto nopesIt = nopesItBegin;
    for (auto fieldIt = fieldItBegin;
         fieldIt != fieldItEnd && nopesIt != nopesItEnd; ++fieldIt, ++nopesIt) {
        if (nopesIt->empty()) {
            continue;
        }
        insertNopes(fieldIt, *nopesIt);
    }
}

template <typename FieldIterator>
void Row::insertSkyscraper(FieldIterator fieldIt, int skyscraper)
{
    assert(mCrossingRows.size() == size());

    if ((*fieldIt)->hasSkyscraper()) {
        return;
    }
    (*fieldIt)->insertSkyscraper(skyscraper);

    if (hasOnlyOneNopeField()) {
        addLastMissingSkyscraper();
    }
    addNopesToAllNopeFields(skyscraper);

    int idx = getIdx(fieldIt);

    if (mCrossingRows(idx)->hasOnlyOneNopeField()) {
        mCrossingRows(idx)->addLastMissingSkyscraper();
    }

    mCrossingRows(idx)->addNopesToAllNopeFields(skyscraper);
}

template <typename FieldIterator>
void Row::insertNope(FieldIterator fieldIt, int nope)
{
    if ((*fieldIt)->hasSkyscraper()) {
        return;
    }
    if ((*fieldIt)->nopes().contains(nope)) {
        return;
    }
    (*fieldIt)->insertNope(nope);

    auto optlastMissingNope = (*fieldIt)->lastMissingNope();
    if (optlastMissingNope) {
        insertSkyscraper(fieldIt, *optlastMissingNope);
    }

    if (onlyOneFieldWithoutNope(nope)) {
        insertSkyscraperToFirstFieldWithoutNope(nope);
    }

    int idx = getIdx(fieldIt);

    if (mCrossingRows(idx)->onlyOneFieldWithoutNope(nope)) {
        mCrossingRows(idx)->insertSkyscraperToFirstFieldWithoutNope(nope);
    }
}

template <typename IteratorType>
void Row::insertNopes(IteratorType it, const std::vector<int> &nopes)
{
    for (const auto &nope : nopes) {
        insertNope(it, nope);
    }
}

int Row::getIdx(std::vector<Field *>::const_iterator cit) const
{
    return std::distance(mRowFields.cbegin(), cit);
}

int Row::getIdx(std::vector<Field *>::const_reverse_iterator crit) const
{
    return size() - std::distance(mRowFields.crbegin(), crit) - 1;
}

std::vector<Field *>
Row::getRowFields(const ReadDirection &readDirection,
                  std::vector<std::vector<Field>> &boardFields,
                  const Point &startPoint)
{
    std::vector<Field *> fields;
    fields.reserve(boardFields.size());
    std::size_t x = startPoint.x;
    std::size_t y = startPoint.y;
    for (std::size_t i = 0; i < boardFields.size(); ++i) {
        fields.emplace_back(&boardFields(y)(x));

        if (readDirection == ReadDirection::topToBottom) {
            ++y;
        }
        else {
            --x;
        }
    }
    return fields;
}

bool Row::onlyOneFieldWithoutNope(int nope) const
{
    auto cit = std::find_if(
        mRowFields.cbegin(), mRowFields.cend(),
        (nope)(const auto &field) { return field->skyscraper() == nope; });
    if (cit != mRowFields.cend()) {
        return false;
    }
    if (nopeCount(nope) < static_cast<int>(size()) - skyscraperCount() - 1) {
        return false;
    }
    return true;
}

std::optional<int> Row::nopeValueInAllButOneField() const
{
    std::unordered_map<int, int> nopeAndCount;

    for (auto cit = mRowFields.cbegin(); cit != mRowFields.cend(); ++cit) {
        if (!(*cit)->hasSkyscraper()) {
            auto nopes = (*cit)->nopes().containing();
            for (const auto &nope : nopes) {
                if (hasSkyscraper(nope)) {
                    continue;
                }
                ++nopeAndCount(nope);
            }
        }
    }
    for (auto cit = nopeAndCount.cbegin(); cit != nopeAndCount.end(); ++cit) {
        if (cit->second == static_cast<int>(size()) - skyscraperCount() - 1) {
            return {cit->first};
        }
    }
    return {};
}

void Row::insertSkyscraperToFirstFieldWithoutNope(int nope)
{
    for (auto it = mRowFields.begin(); it != mRowFields.end(); ++it) {
        if ((*it)->hasSkyscraper()) {
            continue;
        }
        if (!(*it)->nopes().contains(nope)) {
            insertSkyscraper(it, nope);
            return; // there can be max one skyscraper per row;
        }
    }
}

bool Row::hasSkyscraper(int skyscraper) const
{
    for (const auto &field : mRowFields) {
        if (field->skyscraper() == skyscraper) {
            return true;
        }
    }
    return false;
}

class BorderIterator {
public:
    BorderIterator(std::size_t boardSize);

    Point point() const;
    ReadDirection readDirection() const;

    BorderIterator &operator++();

private:
    int mIdx = 0;
    std::size_t mBoardSize;
    Point mPoint{0, 0};
    ReadDirection mReadDirection{ReadDirection::topToBottom};
};

BorderIterator::BorderIterator(std::size_t boardSize) : mBoardSize{boardSize}
{
}

Point BorderIterator::point() const
{
    return mPoint;
}

ReadDirection BorderIterator::readDirection() const
{
    return mReadDirection;
}

BorderIterator &BorderIterator::operator++()
{
    ++mIdx;
    if (mIdx == static_cast<int>(2 * mBoardSize)) {
        return *this;
    }
    if (mIdx != 0 && mIdx % mBoardSize == 0) {
        nextDirection(mReadDirection);
    }

    advanceToNextPosition(mPoint, mReadDirection, mIdx % mBoardSize);
    return *this;
}

struct Board {
    Board(std::size_t size);

    void insert(const std::vector<std::optional<ClueHints>> &clueHints);

    void insert(const std::vector<std::vector<int>> &startingSkyscrapers);

    bool isSolved() const;

    std::vector<std::vector<int>> skyscrapers{};
    std::vector<std::vector<Nopes>> nopes;

    std::vector<Row> mRows;

private:
    std::vector<std::vector<int>> makeSkyscrapers(std::size_t size);
    std::vector<std::vector<Nopes>> makeNopes(std::size_t size);

    void makeFields();
    void makeRows();
    void connnectRowsWithCrossingRows();

    std::vector<std::vector<Field>> mFields;
};

Board::Board(std::size_t size)
    : skyscrapers{makeSkyscrapers(size)}, nopes{makeNopes(size)},
      mFields{std::vector<std::vector<Field>>(skyscrapers.size())}
{
    makeFields();
    makeRows();
}

void Board::insert(const std::vector<std::optional<ClueHints>> &clueHints)
{
    assert(clueHints.size() == mRows.size());

    for (std::size_t i = 0; i < clueHints.size(); ++i) {
        if (!clueHints(i)) {
            continue;
        }
        mRows(i).addNopes(clueHints(i)->nopes, Row::Direction::front);
        mRows(i).addSkyscrapers(clueHints(i)->skyscrapers,
                                Row::Direction::front);
    }
}

void Board::insert(const std::vector<std::vector<int>> &startingSkyscrapers)
{
    if (startingSkyscrapers.empty()) {
        return;
    }
    std::size_t boardSize = mRows.size() / 2;
    assert(startingSkyscrapers.size() == boardSize);
    for (std::size_t i = 0; i < startingSkyscrapers.size(); ++i) {
        mRows(i + boardSize).addSkyscrapers(startingSkyscrapers(i),
                                            Row::Direction::back);
    }
}

bool Board::isSolved() const
{
    std::size_t endVerticalRows = mRows.size() / 2;
    for (std::size_t i = 0; i < endVerticalRows; ++i) {
        if (!mRows(i).allFieldsContainSkyscraper()) {
            return false;
        }
    }
    return true;
}

std::vector<std::vector<int>> Board::makeSkyscrapers(std::size_t size)
{
    std::vector<int> skyscraperRow(size, 0);
    return std::vector<std::vector<int>>(size, skyscraperRow);
}

std::vector<std::vector<Nopes>> Board::makeNopes(std::size_t size)
{
    std::vector<Nopes> nopesRow(size, Nopes{static_cast<int>(size) - 1});
    return std::vector<std::vector<Nopes>>(size, nopesRow);
}

void Board::makeFields()
{
    mFields.reserve(skyscrapers.size());
    for (auto &row : mFields) {
        row.reserve(mFields.size());
    }
    for (std::size_t y = 0; y < skyscrapers.size(); ++y) {
        mFields(y).reserve(skyscrapers.size());
        for (std::size_t x = 0; x < skyscrapers(y).size(); ++x) {
            mFields(y).emplace_back(Field{skyscrapers(y)(x), nopes(y)(x)});
        }
    }
}

void Board::makeRows()
{
    BorderIterator borderIterator{mFields.size()};

    std::size_t size = mFields.size() * 2;
    mRows.reserve(size);

    for (std::size_t i = 0; i < size; ++i, ++borderIterator) {
        mRows.emplace_back(Row{mFields, borderIterator.point(),
                               borderIterator.readDirection()});
    }
    connnectRowsWithCrossingRows();
}

void Board::connnectRowsWithCrossingRows()
{
    std::size_t boardSize = mRows.size() / 2;

    std::vector<int> targetRowsIdx(boardSize);
    std::iota(targetRowsIdx.begin(), targetRowsIdx.end(), boardSize);

    for (std::size_t i = 0; i < mRows.size(); ++i) {
        if (i == mRows.size() / 2) {
            std::iota(targetRowsIdx.begin(), targetRowsIdx.end(), 0);
            std::reverse(targetRowsIdx.begin(), targetRowsIdx.end());
        }

        for (const auto &targetRowIdx : targetRowsIdx) {
            mRows(i).addCrossingRows(&mRows(targetRowIdx));
        }
    }
}

void debug_print(Board &board, const std::string &title)
{
    std::cout << title << 'n';
    for (std::size_t y = 0; y < board.skyscrapers.size(); ++y) {
        for (std::size_t x = 0; x < board.skyscrapers(y).size(); ++x) {

            if (board.skyscrapers(y)(x) != 0) {
                std::cout << std::setw(board.skyscrapers.size() * 2);
                std::cout << "V" + std::to_string(board.skyscrapers(y)(x));
            }
            else if (board.skyscrapers(y)(x) == 0 &&
                     !board.nopes(y)(x).isEmpty()) {
                auto nopes_set = board.nopes(y)(x).values();
                std::vector<int> nopes(nopes_set.begin(), nopes_set.end());
                std::sort(nopes.begin(), nopes.end());

                std::string nopesStr;
                for (std::size_t i = 0; i < nopes.size(); ++i) {
                    nopesStr.append(std::to_string(nopes(i)));
                    if (i != nopes.size() - 1) {
                        nopesStr.push_back(',');
                    }
                }
                std::cout << std::setw(board.skyscrapers.size() * 2);
                std::cout << nopesStr;
            }
            else {
                std::cout << ' ';
            }
        }
        std::cout << 'n';
    }
    std::cout << 'n';
}

template <typename Iterator> int visibleBuildings(Iterator begin, Iterator end)
{
    int visibleBuildingsCount = 0;
    int highestSeen = 0;
    for (auto it = begin; it != end; ++it) {
        if (*it != 0 && *it > highestSeen) {
            ++visibleBuildingsCount;
            highestSeen = *it;
        }
    }
    return visibleBuildingsCount;
}

bool rowsAreValid(const std::vector<std::vector<int>> &skyscrapers,
                  std::size_t x, std::size_t y, std::size_t size)
{
    for (std::size_t xi = 0; xi < size; xi++) {
        if (xi != x && skyscrapers(y)(xi) == skyscrapers(y)(x)) {
            return false;
        }
    }
    return true;
}

bool columnsAreValid(const std::vector<std::vector<int>> &skyscrapers,
                     std::size_t x, std::size_t y, std::size_t size)
{
    for (std::size_t yi = 0; yi < size; yi++) {
        if (yi != y && skyscrapers(yi)(x) == skyscrapers(y)(x)) {
            return false;
        }
    }
    return true;
}

std::tuple<int, int> getRowClues(const std::vector<int> &clues, std::size_t y,
                                 std::size_t size)
{
    int frontClue = clues(clues.size() - 1 - y);
    int backClue = clues(size + y);
    return {frontClue, backClue};
}

bool rowCluesAreValid(const std::vector<std::vector<int>> &skyscrapers,
                      const std::vector<int> &clues, std::size_t y,
                      std::size_t size)
{
    auto (frontClue, backClue) = getRowClues(clues, y, size);

    if (frontClue == 0 && backClue == 0) {
        return true;
    }

    bool rowIsFull = std::find(skyscrapers(y).cbegin(), skyscrapers(y).cend(),
                               0) == skyscrapers(y).cend();

    if (!rowIsFull) {
        return true;
    }

    if (frontClue != 0) {
        auto frontVisible =
            visibleBuildings(skyscrapers(y).cbegin(), skyscrapers(y).cend());

        if (frontClue != frontVisible) {
            return false;
        }
    }
    if (backClue != 0) {
        auto backVisible =
            visibleBuildings(skyscrapers(y).crbegin(), skyscrapers(y).crend());

        if (backClue != backVisible) {
            return false;
        }
    }
    return true;
}

std::tuple<int, int> getColumnClues(const std::vector<int> &clues,
                                    std::size_t x, std::size_t size)
{
    int frontClue = clues(x);
    int backClue = clues(size * 3 - 1 - x);
    return {frontClue, backClue};
}

bool columnCluesAreValid(const std::vector<std::vector<int>> &skyscrapers,
                         const std::vector<int> &clues, std::size_t x,
                         std::size_t size)
{
    auto (frontClue, backClue) = getColumnClues(clues, x, size);

    if (frontClue == 0 && backClue == 0) {
        return true;
    }

    std::vector<int> verticalSkyscrapers;
    verticalSkyscrapers.reserve(size);

    for (std::size_t yi = 0; yi < size; ++yi) {
        verticalSkyscrapers.emplace_back(skyscrapers(yi)(x));
    }

    bool columnIsFull =
        std::find(verticalSkyscrapers.cbegin(), verticalSkyscrapers.cend(),
                  0) == verticalSkyscrapers.cend();

    if (!columnIsFull) {
        return true;
    }

    if (frontClue != 0) {
        auto frontVisible = visibleBuildings(verticalSkyscrapers.cbegin(),
                                             verticalSkyscrapers.cend());
        if (frontClue != frontVisible) {
            return false;
        }
    }
    if (backClue != 0) {
        auto backVisible = visibleBuildings(verticalSkyscrapers.crbegin(),
                                            verticalSkyscrapers.crend());

        if (backClue != backVisible) {
            return false;
        }
    }
    return true;
}

bool skyscrapersAreValidPositioned(
    const std::vector<std::vector<int>> &skyscrapers,
    const std::vector<int> &clues, std::size_t x, std::size_t y,
    std::size_t size)
{
    if (!rowsAreValid(skyscrapers, x, y, size)) {
        return false;
    }
    if (!columnsAreValid(skyscrapers, x, y, size)) {
        return false;
    }
    if (!rowCluesAreValid(skyscrapers, clues, y, size)) {
        return false;
    }
    if (!columnCluesAreValid(skyscrapers, clues, x, size)) {
        return false;
    }
    return true;
}

bool guessSkyscrapers(Board &board, const std::vector<int> &clues,
                      std::size_t x, std::size_t y, std::size_t size)
{
    if (x == size) {
        x = 0;
        y++;
    };
    if (y == size) {
        return true;
    }
    if (board.skyscrapers(y)(x) != 0) {
        if (!skyscrapersAreValidPositioned(board.skyscrapers, clues, x, y,
                                           size)) {
            return false;
        }
        if (guessSkyscrapers(board, clues, x + 1, y, size)) {
            return true;
        }
        else {
            return false;
        }
    }

    for (int trySkyscraper = 1;
         trySkyscraper <= static_cast<int>(board.skyscrapers.size());
         ++trySkyscraper) {

        if (board.nopes(y)(x).contains(trySkyscraper)) {
            continue;
        }
        board.skyscrapers(y)(x) = trySkyscraper;
        if (!skyscrapersAreValidPositioned(board.skyscrapers, clues, x, y,
                                           size)) {
            continue;
        }
        if (guessSkyscrapers(board, clues, x + 1, y, size)) {
            return true;
        }
    }
    board.skyscrapers(y)(x) = 0;
    return false;
}

std::vector<std::vector<int>>
SolvePuzzle(const std::vector<int> &clues,
            std::vector<std::vector<int>> startingGrid, int)
{
    assert(clues.size() % 4 == 0);

    std::size_t boardSize = clues.size() / 4;

    auto clueHints = getClueHints(clues, boardSize);

    Board board{boardSize};

    board.insert(clueHints);
    board.insert(startingGrid);

    if (board.isSolved()) {
        return board.skyscrapers;
    }

    guessSkyscrapers(board, clues, 0, 0, board.skyscrapers.size());
    return board.skyscrapers;
}

std::vector<std::vector<int>> SolvePuzzle(const std::vector<int> &clues)
{
    return SolvePuzzle(clues, std::vector<std::vector<int>>{}, 0);
}

} // namespace codewarsbacktracking

DAE solver fails for system of coupled partial differential equations

I am trying to run the following code to solve a system of coupled differential equation:

ClearAll("Global`*")
(Rho)0 = 1;
M = 1;
eq1 = D((Rho)(r, t), t) R(r, t) D(R(r, t), r) + 
   1/3 D((Rho)(r, t), r) R(r, t) D(R(r, t), t) + 
   10/3  (Rho)(r, t) D(R(r, t), r) + 
   4/3 (Rho)(r, t) R(r, t) D(R(r, t), r, t);
eq2 = Simplify(
   D(R(r, t), r, t) + 
    D(R(r, t), 
      r) (3/2 D((Rho)(r, t), t)/(Rho)(r, t) - 
       4 D(R(r, t), t)/R(r, t)) + 
    D(R(r, t), t) D((Rho)(r, t), r)/(4 (Rho)(r, t)));
{Ro, Ra} = 
 NDSolveValue({eq1 == 0, 
   eq2 == 0, (Rho)(r, 0) == (Rho)0, (Rho)(0, t) == (Rho)0, 
   R(r, 0) == ((3 M)/(4 Pi (Rho)0))^(1/3), 
   R(0, t) == ((3 M)/(4 Pi (Rho)0))^(1/3)}, {(Rho), R}, {r, 0, 
   1}, {t, 0, 1})

but I get the error:

The DAE solver failed at r = 0.`. The solver is intended for index 1
DAE systems and structural analysis indicates that the DAE is
structurally singular

I tried to read this page on DAE, but I did not understand most of it. How can I solve my system?