root/src/apps/sudoku/SudokuField.cpp
/*
 * Copyright 2007-2012, Axel Dörfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */


#include "SudokuField.h"

#include <new>
#include <stdio.h>
#include <string.h>

#include <Message.h>
#include <OS.h>


const char*
mask(uint32 set)
{
        static char text[64];
        uint32 i = 0;
        for (int32 number = 9; number > 0; number--) {
                text[i++] = set & (1UL << (number - 1)) ? number + '0' : '-';
        }

        text[i] = '\0';
        return text;
}


//      #pragma mark -


SudokuField::field::field()
        :
        hint_mask(0),
        valid_mask(~0),
        flags(0),
        value(0)
{
}


//      #pragma mark -


SudokuField::SudokuField(uint32 size)
        :
        fSize(size * size),
        fBlockSize(size)
{
        fFields = new (std::nothrow) field[fSize * fSize];
        fMaxMask = (1UL << fSize) - 1;
}


SudokuField::SudokuField(const BMessage* archive)
{
        if (archive->FindInt32("block size", (int32*)&fBlockSize) != B_OK)
                return;

        fSize = fBlockSize * fBlockSize;
        fMaxMask = (1UL << fSize) - 1;

        uint32 count = fSize * fSize;
        fFields = new (std::nothrow) field[count];
        if (fFields == NULL)
                return;

        for (uint32 i = 0; i < count; i++) {
                struct field& field = fFields[i];

                if (archive->FindInt32("value", i, (int32*)&field.value) != B_OK
                        || archive->FindInt32("valid mask", i,
                                        (int32*)&field.valid_mask) != B_OK
                        || archive->FindInt32("hint mask", i,
                                        (int32*)&field.hint_mask) != B_OK
                        || archive->FindInt32("flags", i, (int32*)&field.flags) != B_OK)
                        break;
        }
}


SudokuField::SudokuField(const SudokuField& other)
        : BArchivable(other)
{
        fSize = other.fSize;
        fBlockSize = other.fBlockSize;
        fMaxMask = other.fMaxMask;

        fFields = new (std::nothrow) field[fSize * fSize];
        if (fFields != NULL)
                memcpy(fFields, other.fFields, sizeof(field) * fSize * fSize);
}


SudokuField::~SudokuField()
{
        delete[] fFields;
}


status_t
SudokuField::InitCheck()
{
        if (fBlockSize == 0)
                return B_BAD_VALUE;
        return fFields == NULL ? B_NO_MEMORY : B_OK;
}


status_t
SudokuField::Archive(BMessage* archive, bool deep) const
{
        status_t status = BArchivable::Archive(archive, deep);
        if (status == B_OK)
                archive->AddInt32("block size", fBlockSize);
        if (status < B_OK)
                return status;

        uint32 count = fSize * fSize;
        for (uint32 i = 0; i < count && status == B_OK; i++) {
                struct field& field = fFields[i];
                status = archive->AddInt32("value", field.value);
                if (status == B_OK)
                        status = archive->AddInt32("valid mask", field.valid_mask);
                if (status == B_OK)
                        status = archive->AddInt32("hint mask", field.hint_mask);
                if (status == B_OK)
                        status = archive->AddInt32("flags", field.flags);
        }

        return status;
}


/*static*/ SudokuField*
SudokuField::Instantiate(BMessage* archive)
{
        if (!validate_instantiation(archive, "SudokuField"))
                return NULL;

        return new SudokuField(archive);
}


void
SudokuField::Reset()
{
        for (uint32 y = 0; y < fSize; y++) {
                for (uint32 x = 0; x < fSize; x++) {
                        struct field& field = _FieldAt(x, y);
                        field.value = 0;
                        field.flags = 0;
                        field.hint_mask = 0;
                        field.valid_mask = fMaxMask;
                }
        }
}


status_t
SudokuField::SetTo(char base, const char* data)
{
        if (data != NULL && strlen(data) < fSize * fSize)
                return B_BAD_VALUE;

        Reset();

        if (data == NULL)
                return B_OK;

        uint32 i = 0;

        for (uint32 y = 0; y < fSize; y++) {
                for (uint32 x = 0; x < fSize; x++) {
                        uint32 value = data[i++] - base;
                        if (value) {
                                struct field& field = _FieldAt(x, y);
                                field.value = value;
                                field.flags = kInitialValue;
                        }
                }
        }

        for (uint32 y = 0; y < fSize; y++) {
                for (uint32 x = 0; x < fSize; x++) {
                        _ComputeValidMask(x, y, false);
                }
        }

        return B_OK;
}


void
SudokuField::SetTo(const SudokuField* field)
{
        if (field == NULL) {
                Reset();
                return;
        }

        for (uint32 y = 0; y < fSize; y++) {
                for (uint32 x = 0; x < fSize; x++) {
                        _FieldAt(x, y) = field->_FieldAt(x, y);
                }
        }
}


void
SudokuField::Dump()
{
        for (uint32 y = 0; y < fSize; y++) {
                for (uint32 x = 0; x < fSize; x++) {
                        if (x != 0 && x % fBlockSize == 0)
                                putchar(' ');
                        printf("%" B_PRIu32, ValueAt(x, y));
                }
                putchar('\n');
        }
}


bool
SudokuField::IsSolved() const
{
        for (uint32 y = 0; y < fSize; y++) {
                for (uint32 x = 0; x < fSize; x++) {
                        if (!_ValidValueAt(x, y))
                                return false;
                }
        }

        return true;
}


bool
SudokuField::IsEmpty() const
{
        for (uint32 y = 0; y < fSize; y++) {
                for (uint32 x = 0; x < fSize; x++) {
                        if (ValueAt(x, y) != 0)
                                return false;
                }
        }

        return true;
}


bool
SudokuField::IsValueCompleted(uint32 value) const
{
        uint32 count = 0;
        for (uint32 y = 0; y < fSize; y++) {
                for (uint32 x = 0; x < fSize; x++) {
                        if (ValueAt(x, y) == value)
                                count++;
                }
        }

        return count == Size();
}


void
SudokuField::SetHintMaskAt(uint32 x, uint32 y, uint32 hintMask)
{
        _FieldAt(x, y).hint_mask = hintMask;
}


uint32
SudokuField::HintMaskAt(uint32 x, uint32 y) const
{
        return _FieldAt(x, y).hint_mask;
}


bool
SudokuField::HasHint(uint32 x, uint32 y, uint32 value) const
{
        return (_FieldAt(x, y).hint_mask & (1UL << (value - 1))) != 0;
}


void
SudokuField::SetValidMaskAt(uint32 x, uint32 y, uint32 validMask)
{
        _FieldAt(x, y).valid_mask = validMask & fMaxMask;
}


uint32
SudokuField::ValidMaskAt(uint32 x, uint32 y) const
{
        return _FieldAt(x, y).valid_mask;
}


bool
SudokuField::IsValid(uint32 x, uint32 y, uint32 value) const
{
        return (_FieldAt(x, y).valid_mask & (1UL << (value - 1))) != 0;
}


void
SudokuField::SetFlagsAt(uint32 x, uint32 y, uint32 flags)
{
        _FieldAt(x, y).flags = flags;
}


uint32
SudokuField::FlagsAt(uint32 x, uint32 y) const
{
        return _FieldAt(x, y).flags;
}


bool
SudokuField::IsInitialValue(uint32 x, uint32 y) const
{
        return (_FieldAt(x, y).flags & kInitialValue) != 0;
}


void
SudokuField::SetValueAt(uint32 x, uint32 y, uint32 value, bool setSolved)
{
        _FieldAt(x, y).value = value;
        _FieldAt(x, y).hint_mask = 0;
        _UpdateValidMaskChanged(x, y, setSolved);
}


uint32
SudokuField::ValueAt(uint32 x, uint32 y) const
{
        return _FieldAt(x, y).value;
}


bool
SudokuField::_ValidValueAt(uint32 x, uint32 y) const
{
        uint32 value = _FieldAt(x, y).value;
        if (!value)
                return false;

        value = 1UL << (value - 1);
        return (_FieldAt(x, y).valid_mask & value) != 0;
}


void
SudokuField::_ComputeValidMask(uint32 x, uint32 y, bool setSolved)
{
        if (ValueAt(x, y))
                return;

        // check row

        uint32 foundMask = 0;
        for (uint32 i = 0; i < fSize; i++) {
                uint32 value = ValueAt(i, y);
                if (value && _ValidValueAt(i, y))
                        foundMask |= 1UL << (value - 1);
        }

        // check column

        for (uint32 i = 0; i < fSize; i++) {
                uint32 value = ValueAt(x, i);
                if (value && _ValidValueAt(x, i))
                        foundMask |= 1UL << (value - 1);
        }

        // check block

        uint32 offsetX = x / fBlockSize * fBlockSize;
        uint32 offsetY = y / fBlockSize * fBlockSize;

        for (uint32 partY = 0; partY < fBlockSize; partY++) {
                for (uint32 partX = 0; partX < fBlockSize; partX++) {
                        uint32 value = ValueAt(partX + offsetX, partY + offsetY);
                        if (value && _ValidValueAt(partX + offsetX, partY + offsetY))
                                foundMask |= 1UL << (value - 1);
                }
        }

        SetValidMaskAt(x, y, ~foundMask);

        if (setSolved) {
                // find the one set bit, if not more
                uint32 value = 0;
                for (uint32 i = 0; i < fSize; i++) {
                        if ((foundMask & (1UL << i)) == 0) {
                                if (value != 0) {
                                        value = 0;
                                        break;
                                }

                                value = i + 1;
                        }
                }
                if (value != 0)
                        SetValueAt(x, y, value, true);
        }
}


void
SudokuField::_UpdateValidMaskChanged(uint32 x, uint32 y, bool setSolved)
{
        // update row

        for (uint32 i = 0; i < fSize; i++) {
                _ComputeValidMask(i, y, setSolved);
        }

        // update column

        for (uint32 i = 0; i < fSize; i++) {
                if (i == y)
                        continue;

                _ComputeValidMask(x, i, setSolved);
        }

        // update block

        uint32 offsetX = x / fBlockSize * fBlockSize;
        uint32 offsetY = y / fBlockSize * fBlockSize;

        for (uint32 partY = 0; partY < fBlockSize; partY++) {
                for (uint32 partX = 0; partX < fBlockSize; partX++) {
                        if (partX + offsetX == x || partY + offsetY == y)
                                continue;

                        _ComputeValidMask(partX + offsetX, partY + offsetY, setSolved);
                }
        }
}


const SudokuField::field&
SudokuField::_FieldAt(uint32 x, uint32 y) const
{
        if (x >= fSize || y >= fSize)
                debugger("field outside bounds");

        return fFields[x + y * fSize];
}


SudokuField::field&
SudokuField::_FieldAt(uint32 x, uint32 y)
{
        if (x >= fSize || y >= fSize)
                debugger("field outside bounds");

        return fFields[x + y * fSize];
}