* Added a first game to the image: Sudoku. It comes with a solver and generator
for three different levels (from "Very Easy" to "Hard"). Have fun! * Feel free to do a nicer icon! * Stack.h is actually verbatim copy of a kernel header which we might want to move to a more public place (like shared). * ProgressWindow is taken from ShowImage, but adapted to suit different needs. * It seems to trigger a bug in the interface kit or app_server: when moving the mouse around, the right border of a field is sometimes lost. This does not happen in BeOS, and there is actually no code that looks responsible for this - it might be an off by one error in the region code, though? git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22056 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
227fb2b11c
commit
d578543aa6
@ -46,10 +46,10 @@ BEOS_BIN = "[" addattr alert arp basename bc beep bison bzip2 cat cardctl catatt
|
||||
;
|
||||
|
||||
|
||||
BEOS_APPS = AboutSystem CodyCam DeskCalc DiskProbe CDPlayer Expander Icon-O-Matic
|
||||
Installer LaunchBox Magnify Mail MediaPlayer MidiPlayer NetworkStatus
|
||||
People PowerStatus ProcessController ShowImage SoundRecorder StyledEdit
|
||||
Terminal TV Workspaces
|
||||
BEOS_APPS = AboutSystem CodyCam DeskCalc DiskProbe CDPlayer Expander
|
||||
Icon-O-Matic Installer LaunchBox Magnify Mail MediaPlayer MidiPlayer
|
||||
NetworkStatus People PowerStatus ProcessController ShowImage SoundRecorder
|
||||
StyledEdit Terminal TV Workspaces
|
||||
;
|
||||
BEOS_PREFERENCES = Appearance Backgrounds DataTranslations E-mail FileTypes
|
||||
Fonts Keyboard Keymap Media Menu Mouse Network Printers Screen ScreenSaver
|
||||
@ -57,7 +57,7 @@ BEOS_PREFERENCES = Appearance Backgrounds DataTranslations E-mail FileTypes
|
||||
;
|
||||
BEOS_DEMOS = BitmapDrawing Chart Clock $(X86_ONLY)Cortex FontDemo
|
||||
$(X86_ONLY)GLDirectMode $(X86_ONLY)GLTeapot Mandelbrot PictureTest
|
||||
Playground Pulse
|
||||
Playground Pulse Sudoku
|
||||
;
|
||||
BEOS_SYSTEM_LIB = libbe.so $(HAIKU_LIBSTDC++) libmedia.so libtracker.so
|
||||
libtranslation.so libnetwork.so libdebug.so libbsd.so libmail.so
|
||||
|
@ -30,6 +30,7 @@ SubInclude HAIKU_TOP src apps resedit ;
|
||||
SubInclude HAIKU_TOP src apps showimage ;
|
||||
SubInclude HAIKU_TOP src apps soundrecorder ;
|
||||
SubInclude HAIKU_TOP src apps stylededit ;
|
||||
SubInclude HAIKU_TOP src apps sudoku ;
|
||||
SubInclude HAIKU_TOP src apps terminal ;
|
||||
SubInclude HAIKU_TOP src apps tracker ;
|
||||
SubInclude HAIKU_TOP src apps tv ;
|
||||
|
16
src/apps/sudoku/Jamfile
Normal file
16
src/apps/sudoku/Jamfile
Normal file
@ -0,0 +1,16 @@
|
||||
SubDir HAIKU_TOP src apps sudoku ;
|
||||
|
||||
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||
|
||||
Application Sudoku :
|
||||
ProgressWindow.cpp
|
||||
Sudoku.cpp
|
||||
SudokuField.cpp
|
||||
SudokuGenerator.cpp
|
||||
SudokuSolver.cpp
|
||||
SudokuView.cpp
|
||||
SudokuWindow.cpp
|
||||
|
||||
: be tracker
|
||||
: Sudoku.rdef
|
||||
;
|
134
src/apps/sudoku/ProgressWindow.cpp
Normal file
134
src/apps/sudoku/ProgressWindow.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "ProgressWindow.h"
|
||||
|
||||
#include <Autolock.h>
|
||||
#include <Button.h>
|
||||
#include <MessageRunner.h>
|
||||
#include <Screen.h>
|
||||
#include <StatusBar.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
static const uint32 kMsgShow = 'show';
|
||||
|
||||
|
||||
ProgressWindow::ProgressWindow(BWindow* referenceWindow,
|
||||
BMessage* abortMessage)
|
||||
: BWindow(BRect(0, 0, 250, 100), "Progress Monitor",
|
||||
B_MODAL_WINDOW_LOOK, B_FLOATING_APP_WINDOW_FEEL,
|
||||
B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS),
|
||||
fRunner(NULL)
|
||||
{
|
||||
BRect rect = Bounds();
|
||||
|
||||
BView *view = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW);
|
||||
view->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
||||
AddChild(view);
|
||||
|
||||
rect = view->Bounds().InsetByCopy(8, 8);
|
||||
fStatusBar = new BStatusBar(rect, "status", NULL, NULL);
|
||||
float width, height;
|
||||
fStatusBar->GetPreferredSize(&width, &height);
|
||||
fStatusBar->ResizeTo(rect.Width(), height);
|
||||
fStatusBar->SetResizingMode(B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT);
|
||||
view->AddChild(fStatusBar);
|
||||
|
||||
if (abortMessage != NULL && referenceWindow) {
|
||||
rect.top += height + 8;
|
||||
BButton* button = new BButton(rect, "abort", "Abort",
|
||||
abortMessage);
|
||||
button->ResizeToPreferred();
|
||||
button->MoveBy((rect.Width() - button->Bounds().Width()) / 2, 0);
|
||||
view->AddChild(button);
|
||||
button->SetTarget(referenceWindow);
|
||||
height = button->Frame().bottom;
|
||||
}
|
||||
|
||||
ResizeTo(Bounds().Width(), height + 8);
|
||||
BRect frame;
|
||||
if (referenceWindow != NULL)
|
||||
frame = referenceWindow->Frame();
|
||||
else
|
||||
frame = BScreen().Frame();
|
||||
|
||||
MoveTo(frame.left + (frame.Width() - Bounds().Width()) / 2,
|
||||
frame.top + (frame.Height() - Bounds().Height()) / 2);
|
||||
|
||||
Run();
|
||||
}
|
||||
|
||||
|
||||
ProgressWindow::~ProgressWindow()
|
||||
{
|
||||
delete fRunner;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ProgressWindow::Start()
|
||||
{
|
||||
BAutolock _(this);
|
||||
|
||||
fRetrievedUpdate = false;
|
||||
fRetrievedShow = false;
|
||||
delete fRunner;
|
||||
|
||||
BMessage show(kMsgShow);
|
||||
fRunner = new BMessageRunner(this, &show, 1000000, 1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ProgressWindow::Stop()
|
||||
{
|
||||
BAutolock _(this);
|
||||
|
||||
delete fRunner;
|
||||
fRunner = NULL;
|
||||
|
||||
if (!IsHidden())
|
||||
Hide();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ProgressWindow::MessageReceived(BMessage *message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case kMsgShow:
|
||||
if (fRetrievedUpdate && IsHidden()) {
|
||||
Show();
|
||||
Minimize(false);
|
||||
}
|
||||
|
||||
fRetrievedShow = true;
|
||||
break;
|
||||
|
||||
case kMsgProgressStatusUpdate:
|
||||
float percent;
|
||||
if (message->FindFloat("percent", &percent) == B_OK)
|
||||
fStatusBar->Update(percent - fStatusBar->CurrentValue());
|
||||
|
||||
const char *text;
|
||||
if (message->FindString("message", &text) == B_OK)
|
||||
fStatusBar->SetText(text);
|
||||
|
||||
fRetrievedUpdate = true;
|
||||
|
||||
if (fRetrievedShow && IsHidden()) {
|
||||
Show();
|
||||
Minimize(false);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BWindow::MessageReceived(message);
|
||||
}
|
||||
}
|
||||
|
34
src/apps/sudoku/ProgressWindow.h
Normal file
34
src/apps/sudoku/ProgressWindow.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef PROGRESS_WINDOW_H
|
||||
#define PROGRESS_WINDOW_H
|
||||
|
||||
|
||||
#include <Window.h>
|
||||
|
||||
class BMessageRunner;
|
||||
class BStatusBar;
|
||||
|
||||
|
||||
class ProgressWindow : public BWindow {
|
||||
public:
|
||||
ProgressWindow(BWindow* referenceWindow, BMessage* abortMessage = NULL);
|
||||
virtual ~ProgressWindow();
|
||||
|
||||
virtual void MessageReceived(BMessage *message);
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
BStatusBar* fStatusBar;
|
||||
BMessageRunner* fRunner;
|
||||
bool fRetrievedUpdate;
|
||||
bool fRetrievedShow;
|
||||
};
|
||||
|
||||
static const uint32 kMsgProgressStatusUpdate = 'SIup';
|
||||
|
||||
#endif // PROGRESS_WINDOW_H
|
78
src/apps/sudoku/Stack.h
Normal file
78
src/apps/sudoku/Stack.h
Normal file
@ -0,0 +1,78 @@
|
||||
/* Stack - a template stack class (plus some handy methods)
|
||||
*
|
||||
* Copyright 2001-2005, Axel Dörfler, axeld@pinc-software.de.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef KERNEL_UTIL_STACK_H
|
||||
#define KERNEL_UTIL_STACK_H
|
||||
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
template<class T> class Stack {
|
||||
public:
|
||||
Stack()
|
||||
:
|
||||
fArray(NULL),
|
||||
fUsed(0),
|
||||
fMax(0)
|
||||
{
|
||||
}
|
||||
|
||||
~Stack()
|
||||
{
|
||||
free(fArray);
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return fUsed == 0;
|
||||
}
|
||||
|
||||
void MakeEmpty()
|
||||
{
|
||||
// could also free the memory
|
||||
fUsed = 0;
|
||||
}
|
||||
|
||||
status_t Push(T value)
|
||||
{
|
||||
if (fUsed >= fMax) {
|
||||
fMax += 16;
|
||||
T *newArray = (T *)realloc(fArray, fMax * sizeof(T));
|
||||
if (newArray == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fArray = newArray;
|
||||
}
|
||||
fArray[fUsed++] = value;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
bool Pop(T *value)
|
||||
{
|
||||
if (fUsed == 0)
|
||||
return false;
|
||||
|
||||
*value = fArray[--fUsed];
|
||||
return true;
|
||||
}
|
||||
|
||||
T *Array()
|
||||
{
|
||||
return fArray;
|
||||
}
|
||||
|
||||
int32 CountItems() const
|
||||
{
|
||||
return fUsed;
|
||||
}
|
||||
|
||||
private:
|
||||
T *fArray;
|
||||
int32 fUsed;
|
||||
int32 fMax;
|
||||
};
|
||||
|
||||
#endif /* KERNEL_UTIL_STACK_H */
|
101
src/apps/sudoku/Sudoku.cpp
Normal file
101
src/apps/sudoku/Sudoku.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "SudokuWindow.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Alert.h>
|
||||
#include <Application.h>
|
||||
#include <TextView.h>
|
||||
|
||||
|
||||
class Sudoku : public BApplication {
|
||||
public:
|
||||
Sudoku();
|
||||
virtual ~Sudoku();
|
||||
|
||||
virtual void ReadyToRun();
|
||||
|
||||
virtual void RefsReceived(BMessage *message);
|
||||
virtual void MessageReceived(BMessage *message);
|
||||
|
||||
virtual void AboutRequested();
|
||||
|
||||
private:
|
||||
SudokuWindow* fWindow;
|
||||
};
|
||||
|
||||
|
||||
const char* kSignature = "application/x-vnd.Haiku-Sudoku";
|
||||
|
||||
|
||||
Sudoku::Sudoku()
|
||||
: BApplication(kSignature)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Sudoku::~Sudoku()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Sudoku::ReadyToRun()
|
||||
{
|
||||
fWindow = new SudokuWindow();
|
||||
fWindow->Show();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Sudoku::RefsReceived(BMessage* message)
|
||||
{
|
||||
fWindow->PostMessage(message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Sudoku::MessageReceived(BMessage* message)
|
||||
{
|
||||
BApplication::MessageReceived(message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Sudoku::AboutRequested()
|
||||
{
|
||||
BAlert *alert = new BAlert("about", "Sudoku\n"
|
||||
"\twritten by Axel Dörfler\n"
|
||||
"\tCopyright 2007, pinc Software.\n", "Ok");
|
||||
BTextView *view = alert->TextView();
|
||||
BFont font;
|
||||
|
||||
view->SetStylable(true);
|
||||
|
||||
view->GetFont(&font);
|
||||
font.SetSize(18);
|
||||
font.SetFace(B_BOLD_FACE);
|
||||
view->SetFontAndColor(0, 6, &font);
|
||||
|
||||
alert->Go();
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
int
|
||||
main(int /*argc*/, char** /*argv*/)
|
||||
{
|
||||
srand(system_time());
|
||||
|
||||
Sudoku sudoku;
|
||||
sudoku.Run();
|
||||
|
||||
return 0;
|
||||
}
|
10
src/apps/sudoku/Sudoku.h
Normal file
10
src/apps/sudoku/Sudoku.h
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef SUDOKU_H
|
||||
#define SUDOKU_H
|
||||
|
||||
extern const char* kSignature;
|
||||
|
||||
#endif // SUDOKU_H
|
45
src/apps/sudoku/Sudoku.rdef
Normal file
45
src/apps/sudoku/Sudoku.rdef
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
resource(1, "BEOS:APP_SIG") #'MIMS' "application/x-vnd.Haiku-Sudoku";
|
||||
|
||||
resource app_version {
|
||||
major = 1,
|
||||
middle = 0,
|
||||
minor = 0,
|
||||
|
||||
variety = B_APPV_BETA,
|
||||
internal = 3,
|
||||
|
||||
short_info = "Sudoku",
|
||||
long_info = "Sudoku ©2007 Haiku, Inc."
|
||||
};
|
||||
|
||||
resource app_flags B_MULTIPLE_LAUNCH;
|
||||
|
||||
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
|
||||
|
||||
resource vector_icon {
|
||||
$"6E636966050500020006023B2B47BB18653D0FA53D225148297046CA1900FFEC"
|
||||
$"4BFFF0A506020006023B3049396B0ABA90833C646E4A101543299500FFFFFFFF"
|
||||
$"FFF289020006023A1DA6393F04BBB5BC3C6B074AEA3648091100F99B05FFFCB2"
|
||||
$"3D040174110404EA29BA52C57A49C4A1C455B663BB2BB63CBADEB689BB770A04"
|
||||
$"BF8D4AC804C24A58BFE6BFFFBE9A0A06302C303E40454C3C4C2A3C250A04302C"
|
||||
$"303E404540320A04302C40324C2A3C250A04403240454C3C4C2A0A0338423C4D"
|
||||
$"3C440A0622422254325C3E513E402E3A0A0422422254325C32490A0422423249"
|
||||
$"3E402E3A0A043249325C3E513E400A063E423E544E5C5A505A3F4A390A06C785"
|
||||
$"BF40C354C2764E495A3F4A39C391BD6F0A043E42C354C276C785BF40C391BD6F"
|
||||
$"0A054151C08BC8834E5C4E49C35DC27A0A053E423E54C08BC8834151C35DC27A"
|
||||
$"0A044E494E5C5A505A3E0E0A040101000A040100023FFDB4B8243D38243D3FFD"
|
||||
$"B443BDB54769F50A04010002B580AB3F95F9C0DD76B427F54CDA2F47CD160A04"
|
||||
$"010002BC39BD3F60BDC00371BBAC3B4BF11B4704060A04010002403517B500E8"
|
||||
$"35926F3EF37647087242923F0A000100024073E92EB44DAF711940084AC5F7BE"
|
||||
$"466E720A00010002B580AB3F95F9C0DD76B427F54CBB2F47CD160A00010002BC"
|
||||
$"39BD3F60BDC00371BBAC3B4BBF1B46D4060A00010002403517B500E835926F3E"
|
||||
$"F37646487242D23F0A0001021A3EF8780000000000003F1885465E1E46C13215"
|
||||
$"FF01178400040A0001021A3EF8780000000000003F1885465E1E46C132001501"
|
||||
$"178600040A010103023EF8780000000000003F1885465E1E46C1320A02010402"
|
||||
$"3EF8780000000000003F1885465E1E46C1320A030105023EF878000000000000"
|
||||
$"3F1885465E1E46C132"
|
||||
};
|
||||
|
||||
#endif // HAIKU_TARGET_PLATFORM_HAIKU
|
||||
|
419
src/apps/sudoku/SudokuField.cpp
Normal file
419
src/apps/sudoku/SudokuField.cpp
Normal file
@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "SudokuField.h"
|
||||
|
||||
#include <new>
|
||||
#include <stdio.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("%lu", ValueAt(x, y));
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SudokuField::IsSolved()
|
||||
{
|
||||
for (uint32 y = 0; y < fSize; y++) {
|
||||
for (uint32 x = 0; x < fSize; x++) {
|
||||
if (!_ValidValueAt(x, y))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
|
74
src/apps/sudoku/SudokuField.h
Normal file
74
src/apps/sudoku/SudokuField.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef SUDOKU_FIELD_H
|
||||
#define SUDOKU_FIELD_H
|
||||
|
||||
|
||||
#include <Archivable.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
enum {
|
||||
kInitialValue = 0x01,
|
||||
};
|
||||
|
||||
class SudokuField : public BArchivable {
|
||||
public:
|
||||
SudokuField(uint32 size);
|
||||
SudokuField(const BMessage* archive);
|
||||
SudokuField(const SudokuField& other);
|
||||
virtual ~SudokuField();
|
||||
|
||||
status_t InitCheck();
|
||||
|
||||
virtual status_t Archive(BMessage* archive, bool deep) const;
|
||||
static SudokuField* Instantiate(BMessage* archive);
|
||||
|
||||
status_t SetTo(char base, const char* data);
|
||||
void SetTo(const SudokuField* other);
|
||||
void Reset();
|
||||
|
||||
bool IsSolved();
|
||||
|
||||
uint32 Size() const { return fSize; }
|
||||
uint32 BlockSize() const { return fBlockSize; }
|
||||
|
||||
void SetHintMaskAt(uint32 x, uint32 y, uint32 hintMask);
|
||||
uint32 HintMaskAt(uint32 x, uint32 y) const;
|
||||
|
||||
void SetValidMaskAt(uint32 x, uint32 y, uint32 validMask);
|
||||
uint32 ValidMaskAt(uint32 x, uint32 y) const;
|
||||
|
||||
void SetFlagsAt(uint32 x, uint32 y, uint32 flags);
|
||||
uint32 FlagsAt(uint32 x, uint32 y) const;
|
||||
|
||||
void SetValueAt(uint32 x, uint32 y, uint32 value, bool setSolved = false);
|
||||
uint32 ValueAt(uint32 x, uint32 y) const;
|
||||
|
||||
void Dump();
|
||||
|
||||
private:
|
||||
struct field {
|
||||
field();
|
||||
|
||||
uint32 hint_mask;
|
||||
uint32 valid_mask;
|
||||
uint32 flags;
|
||||
uint32 value;
|
||||
};
|
||||
|
||||
bool _ValidValueAt(uint32 x, uint32 y) const;
|
||||
void _ComputeValidMask(uint32 x, uint32 y, bool setSolved);
|
||||
void _UpdateValidMaskChanged(uint32 x, uint32 y, bool setSolved);
|
||||
const field& _FieldAt(uint32 x, uint32 y) const;
|
||||
field& _FieldAt(uint32 x, uint32 y);
|
||||
|
||||
uint32 fSize;
|
||||
uint32 fBlockSize;
|
||||
uint32 fMaxMask;
|
||||
field* fFields;
|
||||
};
|
||||
|
||||
#endif // SUDOKU_FIELD_H
|
171
src/apps/sudoku/SudokuGenerator.cpp
Normal file
171
src/apps/sudoku/SudokuGenerator.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "SudokuGenerator.h"
|
||||
|
||||
#include "ProgressWindow.h"
|
||||
#include "SudokuField.h"
|
||||
#include "SudokuSolver.h"
|
||||
|
||||
|
||||
SudokuGenerator::SudokuGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SudokuGenerator::~SudokuGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SudokuGenerator::_HasOnlyOneSolution(SudokuField& field)
|
||||
{
|
||||
SudokuSolver solver(&field);
|
||||
solver.ComputeSolutions();
|
||||
|
||||
return solver.CountSolutions() == 1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuGenerator::_Progress(BMessenger progress, const char* text,
|
||||
float percent)
|
||||
{
|
||||
BMessage update(kMsgProgressStatusUpdate);
|
||||
if (text)
|
||||
update.AddString("message", text);
|
||||
update.AddFloat("percent", percent);
|
||||
progress.SendMessage(&update);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuGenerator::Generate(SudokuField* target, uint32 fieldsLeft,
|
||||
BMessenger progress, volatile bool *quit)
|
||||
{
|
||||
// first step: generate a solved field - random brute force style
|
||||
|
||||
SudokuField field(target->BlockSize());
|
||||
uint32 inputCount = field.Size() * field.Size() / 3;
|
||||
_Progress(progress, "Creating solvable field", 5.f);
|
||||
|
||||
while (!*quit) {
|
||||
field.Reset();
|
||||
|
||||
// generate input field
|
||||
|
||||
uint32 validMask = 0;
|
||||
|
||||
for (uint32 i = 0; i < inputCount; i++) {
|
||||
uint32 x;
|
||||
uint32 y;
|
||||
do {
|
||||
x = rand() % field.Size();
|
||||
y = rand() % field.Size();
|
||||
} while (!*quit && field.ValueAt(x, y) != 0);
|
||||
|
||||
validMask = field.ValidMaskAt(x, y);
|
||||
if (validMask == 0)
|
||||
break;
|
||||
|
||||
uint32 value;
|
||||
do {
|
||||
value = rand() % field.Size();
|
||||
} while (!*quit && (validMask & (1UL << value)) == 0);
|
||||
|
||||
field.SetValueAt(x, y, value + 1);
|
||||
}
|
||||
|
||||
if (validMask == 0)
|
||||
continue;
|
||||
|
||||
// try to solve it
|
||||
|
||||
SudokuSolver solver(&field);
|
||||
solver.ComputeSolutions();
|
||||
if (solver.CountSolutions() > 0) {
|
||||
// choose a random solution
|
||||
field.SetTo(solver.SolutionAt(rand() % solver.CountSolutions()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*quit)
|
||||
return;
|
||||
|
||||
// next step: try to remove as many fields as possible (and wished)
|
||||
// that still have only a single solution
|
||||
|
||||
int32 removeCount = field.Size() * field.Size() - fieldsLeft;
|
||||
bool tried[field.Size() * field.Size()];
|
||||
int32 tries = field.Size() * field.Size() * 3 / 4;
|
||||
memset(tried, 0, sizeof(tried));
|
||||
_Progress(progress, "Searching for removable values", 30.f);
|
||||
|
||||
while (!*quit && removeCount > 0 && tries-- > 0) {
|
||||
SudokuField copy(field);
|
||||
uint32 x;
|
||||
uint32 y;
|
||||
do {
|
||||
x = rand() % field.Size();
|
||||
y = rand() % field.Size();
|
||||
} while (copy.ValueAt(x, y) == 0 || tried[x + y * field.Size()]);
|
||||
|
||||
tried[x + y * field.Size()] = true;
|
||||
copy.SetValueAt(x, y, 0);
|
||||
|
||||
if (_HasOnlyOneSolution(copy)) {
|
||||
_Progress(progress, NULL, 100.f - (70.f * removeCount / 70.f));
|
||||
field.SetTo(©);
|
||||
removeCount--;
|
||||
}
|
||||
}
|
||||
|
||||
if (*quit)
|
||||
return;
|
||||
|
||||
if (tries <= 0) {
|
||||
puts("check all remove");
|
||||
for (uint32 y = 0; y < field.Size(); y++) {
|
||||
for (uint32 x = 0; x < field.Size(); x++) {
|
||||
if (tried[x + y * field.Size()])
|
||||
continue;
|
||||
|
||||
SudokuField copy(field);
|
||||
copy.SetValueAt(x, y, 0);
|
||||
|
||||
if (_HasOnlyOneSolution(copy)) {
|
||||
_Progress(progress, NULL,
|
||||
100.f - (70.f * removeCount / 70.f));
|
||||
field.SetTo(©);
|
||||
|
||||
if (--removeCount <= 0 || *quit)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (removeCount <= 0 || *quit)
|
||||
break;
|
||||
}
|
||||
printf(" remove count = %ld\n", removeCount);
|
||||
}
|
||||
|
||||
// set the remaining values to be initial values
|
||||
|
||||
for (uint32 y = 0; y < field.Size(); y++) {
|
||||
for (uint32 x = 0; x < field.Size(); x++) {
|
||||
if (field.ValueAt(x, y))
|
||||
field.SetFlagsAt(x, y, kInitialValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (*quit)
|
||||
return;
|
||||
|
||||
target->SetTo(&field);
|
||||
}
|
||||
|
30
src/apps/sudoku/SudokuGenerator.h
Normal file
30
src/apps/sudoku/SudokuGenerator.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef SUDOKU_GENERATOR_H
|
||||
#define SUDOKU_GENERATOR_H
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Messenger.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
class SudokuField;
|
||||
|
||||
|
||||
class SudokuGenerator {
|
||||
public:
|
||||
SudokuGenerator();
|
||||
~SudokuGenerator();
|
||||
|
||||
void Generate(SudokuField* field, uint32 fieldsLeft,
|
||||
BMessenger progress, volatile bool *quit);
|
||||
|
||||
private:
|
||||
void _Progress(BMessenger progress, const char* text, float percent);
|
||||
bool _HasOnlyOneSolution(SudokuField& field);
|
||||
};
|
||||
|
||||
#endif // SUDOKU_GENERATOR_H
|
214
src/apps/sudoku/SudokuSolver.cpp
Normal file
214
src/apps/sudoku/SudokuSolver.cpp
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "SudokuSolver.h"
|
||||
|
||||
#include "SudokuField.h"
|
||||
#include "Stack.h"
|
||||
|
||||
|
||||
struct SolutionStep {
|
||||
public:
|
||||
SolutionStep(const SudokuField* field);
|
||||
SolutionStep(const SolutionStep& other);
|
||||
~SolutionStep();
|
||||
|
||||
void ToFirstUnset();
|
||||
bool ToNextUnset();
|
||||
|
||||
void SetSolvedFields();
|
||||
|
||||
SudokuField* Field() { return fField; }
|
||||
uint32 X() { return fX; }
|
||||
uint32 Y() { return fY; }
|
||||
|
||||
private:
|
||||
SudokuField* fField;
|
||||
uint32 fX;
|
||||
uint32 fY;
|
||||
};
|
||||
|
||||
typedef vector<SolutionStep*> StepList;
|
||||
|
||||
|
||||
uint32
|
||||
bit_count(uint32 value)
|
||||
{
|
||||
uint32 count = 0;
|
||||
while (value > 0) {
|
||||
if (value & 1)
|
||||
count++;
|
||||
value >>= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
SolutionStep::SolutionStep(const SudokuField* _field)
|
||||
{
|
||||
fField = new SudokuField(*_field);
|
||||
fX = 0;
|
||||
fY = 0;
|
||||
}
|
||||
|
||||
|
||||
SolutionStep::SolutionStep(const SolutionStep& other)
|
||||
{
|
||||
fField = new SudokuField(*other.fField);
|
||||
fX = other.fX;
|
||||
fY = other.fY;
|
||||
}
|
||||
|
||||
|
||||
SolutionStep::~SolutionStep()
|
||||
{
|
||||
delete fField;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SolutionStep::ToFirstUnset()
|
||||
{
|
||||
for (uint32 y = 0; y < fField->Size(); y++) {
|
||||
for (uint32 x = 0; x < fField->Size(); x++) {
|
||||
if (!fField->ValueAt(x, y)) {
|
||||
uint32 validMask = fField->ValidMaskAt(x, y);
|
||||
if (bit_count(validMask) == 1) {
|
||||
// If the chosen value is already solved, we set its
|
||||
// value here and go on - this makes sure the first
|
||||
// unset we return has actually more than one possible
|
||||
// value
|
||||
uint32 value = 0;
|
||||
while ((validMask & (1UL << value)) == 0) {
|
||||
value++;
|
||||
}
|
||||
fField->SetValueAt(x, y, value + 1, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
fX = x;
|
||||
fY = y;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SolutionStep::ToNextUnset()
|
||||
{
|
||||
for (uint32 y = fY; y < fField->Size(); y++) {
|
||||
for (uint32 x = 0; x < fField->Size(); x++) {
|
||||
if (y == fY && x == 0) {
|
||||
x = fX;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fField->ValueAt(x, y)) {
|
||||
fX = x;
|
||||
fY = y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
SudokuSolver::SudokuSolver(SudokuField* field)
|
||||
:
|
||||
fField(field)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SudokuSolver::SudokuSolver()
|
||||
:
|
||||
fField(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SudokuSolver::~SudokuSolver()
|
||||
{
|
||||
// we don't own the field but the solutions
|
||||
|
||||
for (uint32 i = 0; i < fSolutions.size(); i++) {
|
||||
delete fSolutions[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuSolver::SetTo(SudokuField* field)
|
||||
{
|
||||
fField = field;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuSolver::ComputeSolutions()
|
||||
{
|
||||
Stack<SolutionStep*> stack;
|
||||
SolutionStep* step = new SolutionStep(fField);
|
||||
step->ToFirstUnset();
|
||||
|
||||
stack.Push(step);
|
||||
uint32 count = 0;
|
||||
|
||||
// brute force version
|
||||
|
||||
while (stack.Pop(&step)) {
|
||||
uint32 x = step->X();
|
||||
uint32 y = step->Y();
|
||||
uint32 validMask = step->Field()->ValidMaskAt(x, y);
|
||||
|
||||
count++;
|
||||
|
||||
if (step->ToNextUnset()) {
|
||||
if (validMask != 0) {
|
||||
// generate further steps
|
||||
for (uint32 i = 0; i < fField->Size(); i++) {
|
||||
if ((validMask & (1UL << i)) == 0)
|
||||
continue;
|
||||
|
||||
SolutionStep* next = new SolutionStep(*step);
|
||||
next->Field()->SetValueAt(x, y, i + 1, true);
|
||||
stack.Push(next);
|
||||
}
|
||||
}
|
||||
} else if (step->Field()->IsSolved())
|
||||
fSolutions.push_back(new SudokuField(*step->Field()));
|
||||
|
||||
delete step;
|
||||
}
|
||||
|
||||
//printf("evaluated %lu steps\n", count);
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
SudokuSolver::CountSolutions()
|
||||
{
|
||||
return fSolutions.size();
|
||||
}
|
||||
|
||||
|
||||
SudokuField*
|
||||
SudokuSolver::SolutionAt(uint32 index)
|
||||
{
|
||||
return fSolutions[index];
|
||||
}
|
||||
|
35
src/apps/sudoku/SudokuSolver.h
Normal file
35
src/apps/sudoku/SudokuSolver.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef SUDOKU_SOLVER_H
|
||||
#define SUDOKU_SOLVER_H
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
class SudokuField;
|
||||
|
||||
class SudokuSolver {
|
||||
public:
|
||||
SudokuSolver(SudokuField* field);
|
||||
SudokuSolver();
|
||||
~SudokuSolver();
|
||||
|
||||
void SetTo(SudokuField* field);
|
||||
|
||||
void ComputeSolutions();
|
||||
|
||||
uint32 CountSolutions();
|
||||
SudokuField* SolutionAt(uint32 index);
|
||||
|
||||
private:
|
||||
typedef vector<SudokuField*> SudokuList;
|
||||
|
||||
SudokuField* fField;
|
||||
SudokuList fSolutions;
|
||||
};
|
||||
|
||||
#endif // SUDOKU_SOLVER_H
|
804
src/apps/sudoku/SudokuView.cpp
Normal file
804
src/apps/sudoku/SudokuView.cpp
Normal file
@ -0,0 +1,804 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "SudokuView.h"
|
||||
|
||||
#include "SudokuField.h"
|
||||
#include "SudokuSolver.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Application.h>
|
||||
#include <File.h>
|
||||
#include <Path.h>
|
||||
|
||||
|
||||
const uint32 kMsgCheckSolved = 'chks';
|
||||
|
||||
const uint32 kStrongLineSize = 2;
|
||||
|
||||
|
||||
SudokuView::SudokuView(BRect frame, const char* name,
|
||||
const BMessage& settings, uint32 resizingMode)
|
||||
: BView(frame, name, resizingMode,
|
||||
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
|
||||
fField(NULL),
|
||||
fShowHintX(~0UL),
|
||||
fLastHintValue(~0UL),
|
||||
fLastField(~0UL),
|
||||
fShowKeyboardFocus(false),
|
||||
fEditable(true)
|
||||
{
|
||||
BMessage field;
|
||||
if (settings.FindMessage("field", &field) == B_OK) {
|
||||
fField = new SudokuField(&field);
|
||||
if (fField->InitCheck() != B_OK) {
|
||||
delete fField;
|
||||
fField = NULL;
|
||||
} else if (fField->IsSolved())
|
||||
ClearAll();
|
||||
}
|
||||
if (fField == NULL)
|
||||
fField = new SudokuField(3);
|
||||
|
||||
fBlockSize = fField->BlockSize();
|
||||
|
||||
if (settings.FindInt32("hint flags", (int32*)&fHintFlags) != B_OK)
|
||||
fHintFlags = kMarkInvalid;
|
||||
if (settings.FindBool("show cursor", &fShowCursor) != B_OK)
|
||||
fShowCursor = false;
|
||||
|
||||
SetViewColor(255, 255, 240);
|
||||
SetLowColor(ViewColor());
|
||||
FrameResized(0, 0);
|
||||
}
|
||||
|
||||
|
||||
SudokuView::~SudokuView()
|
||||
{
|
||||
delete fField;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SudokuView::SaveState(BMessage& state)
|
||||
{
|
||||
BMessage field;
|
||||
status_t status = fField->Archive(&field, true);
|
||||
if (status == B_OK)
|
||||
status = state.AddMessage("field", &field);
|
||||
if (status == B_OK)
|
||||
status = state.AddInt32("hint flags", fHintFlags);
|
||||
if (status == B_OK)
|
||||
status = state.AddBool("show cursor", fShowCursor);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SudokuView::_FilterString(const char* data, size_t dataLength, char* buffer,
|
||||
uint32& out, bool& ignore)
|
||||
{
|
||||
uint32 maxOut = fField->Size() * fField->Size();
|
||||
|
||||
for (uint32 i = 0; data[i] && i < dataLength; i++) {
|
||||
if (data[i] == '#')
|
||||
ignore = true;
|
||||
else if (data[i] == '\n')
|
||||
ignore = false;
|
||||
|
||||
if (ignore || isspace(data[i]))
|
||||
continue;
|
||||
|
||||
if (!_ValidCharacter(data[i])) {
|
||||
return B_BAD_DATA;
|
||||
}
|
||||
|
||||
buffer[out++] = data[i];
|
||||
if (out == maxOut)
|
||||
break;
|
||||
}
|
||||
|
||||
buffer[out] = '\0';
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SudokuView::SetTo(entry_ref& ref)
|
||||
{
|
||||
BPath path;
|
||||
status_t status = path.SetTo(&ref);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
FILE* file = fopen(path.Path(), "r");
|
||||
if (file == NULL)
|
||||
return errno;
|
||||
|
||||
uint32 maxOut = fField->Size() * fField->Size();
|
||||
char buffer[1024];
|
||||
char line[1024];
|
||||
bool ignore = false;
|
||||
uint32 out = 0;
|
||||
|
||||
while (fgets(line, sizeof(line), file) != NULL
|
||||
&& out < maxOut) {
|
||||
status = _FilterString(line, sizeof(line), buffer, out, ignore);
|
||||
if (status < B_OK) {
|
||||
fclose(file);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
status = fField->SetTo(_BaseCharacter(), buffer);
|
||||
Invalidate();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SudokuView::SetTo(const char* data)
|
||||
{
|
||||
if (data == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
char buffer[1024];
|
||||
bool ignore = false;
|
||||
uint32 out = 0;
|
||||
|
||||
status_t status = _FilterString(data, 65536, buffer, out, ignore);
|
||||
if (status < B_OK)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
status = fField->SetTo(_BaseCharacter(), buffer);
|
||||
Invalidate();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SudokuView::SetTo(SudokuField* field)
|
||||
{
|
||||
if (field == NULL || field == fField)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
delete fField;
|
||||
fField = field;
|
||||
|
||||
fBlockSize = fField->BlockSize();
|
||||
FrameResized(0, 0);
|
||||
Invalidate();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SudokuView::SaveTo(entry_ref& ref, bool asText)
|
||||
{
|
||||
BFile file;
|
||||
status_t status = file.SetTo(&ref, B_WRITE_ONLY | B_CREATE_FILE
|
||||
| B_ERASE_FILE);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
if (asText) {
|
||||
char line[1024];
|
||||
strcpy(line, "# Written by Sudoku\n\n");
|
||||
file.Write(line, strlen(line));
|
||||
|
||||
uint32 i = 0;
|
||||
|
||||
for (uint32 y = 0; y < fField->Size(); y++) {
|
||||
for (uint32 x = 0; x < fField->Size(); x++) {
|
||||
if (x != 0 && x % fBlockSize == 0)
|
||||
line[i++] = ' ';
|
||||
_SetText(&line[i++], fField->ValueAt(x, y));
|
||||
}
|
||||
line[i++] = '\n';
|
||||
}
|
||||
|
||||
file.Write(line, i);
|
||||
} else {
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::ClearChanged()
|
||||
{
|
||||
for (uint32 y = 0; y < fField->Size(); y++) {
|
||||
for (uint32 x = 0; x < fField->Size(); x++) {
|
||||
if ((fField->FlagsAt(x, y) & kInitialValue) == 0)
|
||||
fField->SetValueAt(x, y, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::ClearAll()
|
||||
{
|
||||
fField->Reset();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::SetHintFlags(uint32 flags)
|
||||
{
|
||||
if (flags == fHintFlags)
|
||||
return;
|
||||
|
||||
if ((flags & kMarkInvalid) ^ (fHintFlags & kMarkInvalid))
|
||||
Invalidate();
|
||||
|
||||
fHintFlags = flags;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::SetEditable(bool editable)
|
||||
{
|
||||
fEditable = editable;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::AttachedToWindow()
|
||||
{
|
||||
MakeFocus(true);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::_FitFont(BFont& font, float fieldWidth, float fieldHeight)
|
||||
{
|
||||
font.SetSize(100);
|
||||
|
||||
font_height fontHeight;
|
||||
font.GetHeight(&fontHeight);
|
||||
|
||||
float width = font.StringWidth("W");
|
||||
float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
|
||||
|
||||
float factor = fieldWidth != fHintWidth ? 4.f / 5.f : 1.f;
|
||||
float widthFactor = fieldWidth / (width / factor);
|
||||
float heightFactor = fieldHeight / (height / factor);
|
||||
font.SetSize(100 * min_c(widthFactor, heightFactor));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::FrameResized(float /*width*/, float /*height*/)
|
||||
{
|
||||
// font for numbers
|
||||
|
||||
uint32 size = fField->Size();
|
||||
fWidth = (Bounds().Width() - kStrongLineSize * (fBlockSize - 1)) / size;
|
||||
fHeight = (Bounds().Height() - kStrongLineSize * (fBlockSize - 1)) / size;
|
||||
_FitFont(fFieldFont, fWidth - 2, fHeight - 2);
|
||||
|
||||
font_height fontHeight;
|
||||
fFieldFont.GetHeight(&fontHeight);
|
||||
fBaseline = ceilf(fontHeight.ascent) / 2
|
||||
+ (fHeight - ceilf(fontHeight.descent)) / 2;
|
||||
|
||||
// font for hint
|
||||
|
||||
fHintWidth = (fWidth - 2) / fBlockSize;
|
||||
fHintHeight = (fHeight - 2) / fBlockSize;
|
||||
_FitFont(fHintFont, fHintWidth, fHintHeight);
|
||||
|
||||
fHintFont.GetHeight(&fontHeight);
|
||||
fHintBaseline = ceilf(fontHeight.ascent) / 2
|
||||
+ (fHintHeight - ceilf(fontHeight.descent)) / 2;
|
||||
}
|
||||
|
||||
|
||||
BPoint
|
||||
SudokuView::_LeftTop(uint32 x, uint32 y)
|
||||
{
|
||||
return BPoint(x * fWidth + x / fBlockSize * kStrongLineSize + 1,
|
||||
y * fHeight + y / fBlockSize * kStrongLineSize + 1);
|
||||
}
|
||||
|
||||
|
||||
BRect
|
||||
SudokuView::_Frame(uint32 x, uint32 y)
|
||||
{
|
||||
BPoint leftTop = _LeftTop(x, y);
|
||||
BPoint rightBottom = leftTop + BPoint(fWidth - 2, fHeight - 2);
|
||||
|
||||
return BRect(leftTop, rightBottom);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::_InvalidateHintField(uint32 x, uint32 y, uint32 hintX,
|
||||
uint32 hintY)
|
||||
{
|
||||
BPoint leftTop = _LeftTop(x, y);
|
||||
leftTop.x += hintX * fHintWidth;
|
||||
leftTop.y += hintY * fHintHeight;
|
||||
BPoint rightBottom = leftTop;
|
||||
rightBottom.x += fHintWidth;
|
||||
rightBottom.y += fHintHeight;
|
||||
|
||||
Invalidate(BRect(leftTop, rightBottom));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::_InvalidateField(uint32 x, uint32 y)
|
||||
{
|
||||
Invalidate(_Frame(x, y));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::_InvalidateKeyboardFocus(uint32 x, uint32 y)
|
||||
{
|
||||
BRect frame = _Frame(x, y);
|
||||
frame.InsetBy(-1, -1);
|
||||
Invalidate(frame);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SudokuView::_GetHintFieldFor(BPoint where, uint32 x, uint32 y,
|
||||
uint32& hintX, uint32& hintY)
|
||||
{
|
||||
BPoint leftTop = _LeftTop(x, y);
|
||||
hintX = (uint32)floor((where.x - leftTop.x) / fHintWidth);
|
||||
hintY = (uint32)floor((where.y - leftTop.y) / fHintHeight);
|
||||
|
||||
if (hintX >= fBlockSize || hintY >= fBlockSize)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SudokuView::_GetFieldFor(BPoint where, uint32& x, uint32& y)
|
||||
{
|
||||
float block = fWidth * fBlockSize + kStrongLineSize;
|
||||
x = (uint32)floor(where.x / block);
|
||||
uint32 offsetX = (uint32)floor((where.x - x * block) / fWidth);
|
||||
x = x * fBlockSize + offsetX;
|
||||
|
||||
block = fHeight * fBlockSize + kStrongLineSize;
|
||||
y = (uint32)floor(where.y / block);
|
||||
uint32 offsetY = (uint32)floor((where.y - y * block) / fHeight);
|
||||
y = y * fBlockSize + offsetY;
|
||||
|
||||
if (offsetX >= fBlockSize || offsetY >= fBlockSize
|
||||
|| x >= fField->Size() || y >= fField->Size())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::_RemoveHint()
|
||||
{
|
||||
if (fShowHintX == ~0UL)
|
||||
return;
|
||||
|
||||
uint32 x = fShowHintX;
|
||||
uint32 y = fShowHintY;
|
||||
fShowHintX = ~0;
|
||||
fShowHintY = ~0;
|
||||
|
||||
_InvalidateField(x, y);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::MouseDown(BPoint where)
|
||||
{
|
||||
uint32 x, y;
|
||||
if (!fEditable || !_GetFieldFor(where, x, y))
|
||||
return;
|
||||
|
||||
int32 buttons = B_PRIMARY_MOUSE_BUTTON;
|
||||
int32 clicks = 1;
|
||||
if (Looper() != NULL && Looper()->CurrentMessage() != NULL) {
|
||||
Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
|
||||
Looper()->CurrentMessage()->FindInt32("clicks", &clicks);
|
||||
}
|
||||
|
||||
uint32 hintX, hintY;
|
||||
if (!_GetHintFieldFor(where, x, y, hintX, hintY))
|
||||
return;
|
||||
|
||||
uint32 value = hintX + hintY * fBlockSize;
|
||||
uint32 field = x + y * fField->Size();
|
||||
|
||||
if (clicks == 2 && fLastHintValue == value && fLastField == field
|
||||
|| (buttons & (B_SECONDARY_MOUSE_BUTTON
|
||||
| B_TERTIARY_MOUSE_BUTTON)) != 0) {
|
||||
// double click
|
||||
if ((fField->FlagsAt(x, y) & kInitialValue) == 0) {
|
||||
if (fField->ValueAt(x, y) > 0) {
|
||||
fField->SetValueAt(x, y, 0);
|
||||
fShowHintX = x;
|
||||
fShowHintY = y;
|
||||
} else {
|
||||
fField->SetValueAt(x, y, value + 1);
|
||||
BMessenger(this).SendMessage(kMsgCheckSolved);
|
||||
}
|
||||
|
||||
_InvalidateField(x, y);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 hintMask = fField->HintMaskAt(x, y);
|
||||
uint32 valueMask = 1UL << value;
|
||||
if (hintMask & valueMask)
|
||||
hintMask &= ~valueMask;
|
||||
else
|
||||
hintMask |= valueMask;
|
||||
|
||||
fField->SetHintMaskAt(x, y, hintMask);
|
||||
_InvalidateHintField(x, y, hintX, hintY);
|
||||
|
||||
fLastHintValue = value;
|
||||
fLastField = field;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* dragMessage)
|
||||
{
|
||||
if (transit == B_EXITED_VIEW || dragMessage != NULL) {
|
||||
_RemoveHint();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fShowKeyboardFocus) {
|
||||
fShowKeyboardFocus = false;
|
||||
_InvalidateKeyboardFocus(fKeyboardX, fKeyboardY);
|
||||
}
|
||||
|
||||
uint32 x, y;
|
||||
if (!_GetFieldFor(where, x, y)
|
||||
|| (fField->FlagsAt(x, y) & kInitialValue) != 0
|
||||
|| !fShowCursor && fField->ValueAt(x, y) != 0) {
|
||||
_RemoveHint();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fShowHintX == x && fShowHintY == y)
|
||||
return;
|
||||
|
||||
_RemoveHint();
|
||||
fShowHintX = x;
|
||||
fShowHintY = y;
|
||||
_InvalidateField(x, y);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::_InsertKey(char rawKey, int32 modifiers)
|
||||
{
|
||||
if (!fEditable || !_ValidCharacter(rawKey)
|
||||
|| (fField->FlagsAt(fKeyboardX, fKeyboardY) & kInitialValue) != 0)
|
||||
return;
|
||||
|
||||
uint32 value = rawKey - _BaseCharacter();
|
||||
|
||||
if (modifiers & (B_SHIFT_KEY | B_OPTION_KEY)) {
|
||||
// set or remove hint
|
||||
if (value == 0)
|
||||
return;
|
||||
|
||||
uint32 hintMask = fField->HintMaskAt(fKeyboardX, fKeyboardY);
|
||||
uint32 valueMask = 1UL << (value - 1);
|
||||
if (modifiers & B_OPTION_KEY)
|
||||
hintMask &= ~valueMask;
|
||||
else
|
||||
hintMask |= valueMask;
|
||||
|
||||
fField->SetValueAt(fKeyboardX, fKeyboardY, 0);
|
||||
fField->SetHintMaskAt(fKeyboardX, fKeyboardY, hintMask);
|
||||
} else {
|
||||
fField->SetValueAt(fKeyboardX, fKeyboardY, value);
|
||||
if (value)
|
||||
BMessenger(this).SendMessage(kMsgCheckSolved);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::KeyDown(const char *bytes, int32 /*numBytes*/)
|
||||
{
|
||||
be_app->ObscureCursor();
|
||||
|
||||
uint32 x = fKeyboardX, y = fKeyboardY;
|
||||
|
||||
switch (bytes[0]) {
|
||||
case B_UP_ARROW:
|
||||
if (fKeyboardY == 0)
|
||||
fKeyboardY = fField->Size() - 1;
|
||||
else
|
||||
fKeyboardY--;
|
||||
break;
|
||||
case B_DOWN_ARROW:
|
||||
if (fKeyboardY == fField->Size() - 1)
|
||||
fKeyboardY = 0;
|
||||
else
|
||||
fKeyboardY++;
|
||||
break;
|
||||
|
||||
case B_LEFT_ARROW:
|
||||
if (fKeyboardX == 0)
|
||||
fKeyboardX = fField->Size() - 1;
|
||||
else
|
||||
fKeyboardX--;
|
||||
break;
|
||||
case B_RIGHT_ARROW:
|
||||
if (fKeyboardX == fField->Size() - 1)
|
||||
fKeyboardX = 0;
|
||||
else
|
||||
fKeyboardX++;
|
||||
break;
|
||||
|
||||
case B_BACKSPACE:
|
||||
case B_DELETE:
|
||||
case B_SPACE:
|
||||
// clear value
|
||||
_InsertKey(_BaseCharacter(), 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
int32 rawKey = bytes[0];
|
||||
int32 modifiers = 0;
|
||||
if (Looper() != NULL && Looper()->CurrentMessage() != NULL) {
|
||||
Looper()->CurrentMessage()->FindInt32("raw_char", &rawKey);
|
||||
Looper()->CurrentMessage()->FindInt32("modifiers", &modifiers);
|
||||
}
|
||||
|
||||
_InsertKey(rawKey, modifiers);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!fShowKeyboardFocus && fShowHintX != ~0UL) {
|
||||
// always start at last mouse position, if any
|
||||
fKeyboardX = fShowHintX;
|
||||
fKeyboardY = fShowHintY;
|
||||
}
|
||||
|
||||
_RemoveHint();
|
||||
|
||||
// remove old focus, if any
|
||||
if (fShowKeyboardFocus && (x != fKeyboardX || y != fKeyboardY))
|
||||
_InvalidateKeyboardFocus(x, y);
|
||||
|
||||
fShowKeyboardFocus = true;
|
||||
_InvalidateKeyboardFocus(fKeyboardX, fKeyboardY);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::MessageReceived(BMessage* message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case kMsgCheckSolved:
|
||||
if (fField->IsSolved()) {
|
||||
// notify window
|
||||
Looper()->PostMessage(kMsgSudokuSolved);
|
||||
}
|
||||
break;
|
||||
|
||||
case kMsgSolveSudoku:
|
||||
{
|
||||
SudokuSolver solver;
|
||||
solver.SetTo(fField);
|
||||
bigtime_t start = system_time();
|
||||
solver.ComputeSolutions();
|
||||
printf("found %ld solutions in %g msecs\n",
|
||||
solver.CountSolutions(), (system_time() - start) / 1000.0);
|
||||
if (solver.CountSolutions() > 0) {
|
||||
fField->SetTo(solver.SolutionAt(0));
|
||||
Invalidate();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kMsgSolveSingle:
|
||||
{
|
||||
SudokuSolver solver;
|
||||
solver.SetTo(fField);
|
||||
bigtime_t start = system_time();
|
||||
solver.ComputeSolutions();
|
||||
printf("found %ld solutions in %g msecs\n",
|
||||
solver.CountSolutions(), (system_time() - start) / 1000.0);
|
||||
if (solver.CountSolutions() > 0) {
|
||||
// find free spot
|
||||
uint32 x, y;
|
||||
do {
|
||||
x = rand() % fField->Size();
|
||||
y = rand() % fField->Size();
|
||||
} while (fField->ValueAt(x, y));
|
||||
|
||||
fField->SetValueAt(x, y,
|
||||
solver.SolutionAt(0)->ValueAt(x, y));
|
||||
_InvalidateField(x, y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
BView::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char
|
||||
SudokuView::_BaseCharacter()
|
||||
{
|
||||
return fField->Size() > 9 ? '@' : '0';
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SudokuView::_ValidCharacter(char c)
|
||||
{
|
||||
char min = _BaseCharacter();
|
||||
char max = min + fField->Size();
|
||||
return c >= min && c <= max;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::_SetText(char* text, uint32 value)
|
||||
{
|
||||
text[0] = value + _BaseCharacter();
|
||||
text[1] = '\0';
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::_DrawKeyboardFocus()
|
||||
{
|
||||
BRect frame = _Frame(fKeyboardX, fKeyboardY);
|
||||
SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
|
||||
StrokeRect(frame);
|
||||
frame.InsetBy(-1, -1);
|
||||
StrokeRect(frame);
|
||||
frame.InsetBy(2, 2);
|
||||
StrokeRect(frame);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::_DrawHints(uint32 x, uint32 y)
|
||||
{
|
||||
bool showAll = fShowHintX == x && fShowHintY == y;
|
||||
uint32 hintMask = fField->HintMaskAt(x, y);
|
||||
if (hintMask == 0 && !showAll)
|
||||
return;
|
||||
|
||||
uint32 validMask = fField->ValidMaskAt(x, y);
|
||||
BPoint leftTop = _LeftTop(x, y);
|
||||
SetFont(&fHintFont);
|
||||
|
||||
for (uint32 j = 0; j < fBlockSize; j++) {
|
||||
for (uint32 i = 0; i < fBlockSize; i++) {
|
||||
uint32 value = j * fBlockSize + i;
|
||||
if (hintMask & (1UL << value))
|
||||
SetHighColor(200, 0, 0);
|
||||
else {
|
||||
if (!showAll)
|
||||
continue;
|
||||
|
||||
if ((fHintFlags & kMarkValidHints) == 0
|
||||
|| validMask & (1UL << value))
|
||||
SetHighColor(110, 110, 80);
|
||||
else
|
||||
SetHighColor(180, 180, 120);
|
||||
}
|
||||
|
||||
char text[2];
|
||||
_SetText(text, value + 1);
|
||||
DrawString(text, leftTop + BPoint((i + 0.5f) * fHintWidth
|
||||
- StringWidth(text) / 2, floorf(j * fHintHeight)
|
||||
+ fHintBaseline));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuView::Draw(BRect /*updateRect*/)
|
||||
{
|
||||
// draw lines
|
||||
|
||||
uint32 size = fField->Size();
|
||||
|
||||
SetHighColor(0, 0, 0);
|
||||
|
||||
float width = fWidth;
|
||||
for (uint32 x = 1; x < size; x++) {
|
||||
if (x % fBlockSize == 0) {
|
||||
FillRect(BRect(width, 0, width + kStrongLineSize,
|
||||
Bounds().Height()));
|
||||
width += kStrongLineSize;
|
||||
} else {
|
||||
StrokeLine(BPoint(width, 0), BPoint(width, Bounds().Height()));
|
||||
}
|
||||
width += fWidth;
|
||||
}
|
||||
|
||||
float height = fHeight;
|
||||
for (uint32 y = 1; y < size; y++) {
|
||||
if (y % fBlockSize == 0) {
|
||||
FillRect(BRect(0, height, Bounds().Width(),
|
||||
height + kStrongLineSize));
|
||||
height += kStrongLineSize;
|
||||
} else {
|
||||
StrokeLine(BPoint(0, height), BPoint(Bounds().Width(), height));
|
||||
}
|
||||
height += fHeight;
|
||||
}
|
||||
|
||||
// draw text
|
||||
|
||||
for (uint32 y = 0; y < size; y++) {
|
||||
for (uint32 x = 0; x < size; x++) {
|
||||
if ((fShowCursor && x == fShowHintX && y == fShowHintY
|
||||
|| fShowKeyboardFocus && x == fKeyboardX
|
||||
&& y == fKeyboardY)
|
||||
&& (fField->FlagsAt(x, y) & kInitialValue) == 0) {
|
||||
//SetLowColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
|
||||
SetLowColor(255, 255, 210);
|
||||
FillRect(_Frame(x, y), B_SOLID_LOW);
|
||||
} else
|
||||
SetLowColor(ViewColor());
|
||||
|
||||
if (fShowKeyboardFocus && x == fKeyboardX && y == fKeyboardY)
|
||||
_DrawKeyboardFocus();
|
||||
|
||||
uint32 value = fField->ValueAt(x, y);
|
||||
if (value == 0) {
|
||||
_DrawHints(x, y);
|
||||
continue;
|
||||
}
|
||||
|
||||
SetFont(&fFieldFont);
|
||||
if (fField->FlagsAt(x, y) & kInitialValue)
|
||||
SetHighColor(0, 0, 0);
|
||||
else {
|
||||
if ((fHintFlags & kMarkInvalid) == 0
|
||||
|| fField->ValidMaskAt(x, y) & (1UL << (value - 1)))
|
||||
SetHighColor(0, 0, 200);
|
||||
else
|
||||
SetHighColor(200, 0, 0);
|
||||
}
|
||||
|
||||
char text[2];
|
||||
_SetText(text, value);
|
||||
DrawString(text, _LeftTop(x, y)
|
||||
+ BPoint((fWidth - StringWidth(text)) / 2, fBaseline));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
98
src/apps/sudoku/SudokuView.h
Normal file
98
src/apps/sudoku/SudokuView.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef SUDOKU_VIEW_H
|
||||
#define SUDOKU_VIEW_H
|
||||
|
||||
|
||||
#include <View.h>
|
||||
|
||||
class SudokuField;
|
||||
|
||||
|
||||
enum {
|
||||
kMarkValidHints = 0x01,
|
||||
kMarkInvalid = 0x02,
|
||||
};
|
||||
|
||||
|
||||
class SudokuView : public BView {
|
||||
public:
|
||||
SudokuView(BRect frame, const char* name, const BMessage& settings,
|
||||
uint32 resizingMode);
|
||||
virtual ~SudokuView();
|
||||
|
||||
status_t SaveState(BMessage& state);
|
||||
|
||||
status_t SetTo(entry_ref& ref);
|
||||
status_t SetTo(const char* data);
|
||||
status_t SetTo(SudokuField* field);
|
||||
|
||||
status_t SaveTo(entry_ref& ref, bool asText);
|
||||
|
||||
void ClearChanged();
|
||||
void ClearAll();
|
||||
|
||||
void SetHintFlags(uint32 flags);
|
||||
uint32 HintFlags() const { return fHintFlags; }
|
||||
|
||||
SudokuField* Field() { return fField; }
|
||||
|
||||
void SetEditable(bool editable);
|
||||
bool Editable() const { return fEditable; }
|
||||
|
||||
protected:
|
||||
virtual void AttachedToWindow();
|
||||
|
||||
virtual void FrameResized(float width, float height);
|
||||
virtual void MouseDown(BPoint where);
|
||||
virtual void MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* dragMessage);
|
||||
virtual void KeyDown(const char *bytes, int32 numBytes);
|
||||
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
virtual void Draw(BRect updateRect);
|
||||
|
||||
private:
|
||||
status_t _FilterString(const char* data, size_t dataLength, char* buffer,
|
||||
uint32& out, bool& ignore);
|
||||
void _SetText(char* text, uint32 value);
|
||||
char _BaseCharacter();
|
||||
bool _ValidCharacter(char c);
|
||||
BPoint _LeftTop(uint32 x, uint32 y);
|
||||
BRect _Frame(uint32, uint32 y);
|
||||
void _InvalidateHintField(uint32 x, uint32 y, uint32 hintX, uint32 hintY);
|
||||
void _InvalidateField(uint32 x, uint32 y);
|
||||
void _InvalidateKeyboardFocus(uint32 x, uint32 y);
|
||||
void _InsertKey(char rawKey, int32 modifiers);
|
||||
void _RemoveHint();
|
||||
bool _GetHintFieldFor(BPoint where, uint32 x, uint32 y,
|
||||
uint32& hintX, uint32& hintY);
|
||||
bool _GetFieldFor(BPoint where, uint32& x, uint32& y);
|
||||
void _FitFont(BFont& font, float width, float height);
|
||||
void _DrawKeyboardFocus();
|
||||
void _DrawHints(uint32 x, uint32 y);
|
||||
|
||||
SudokuField* fField;
|
||||
uint32 fBlockSize;
|
||||
float fWidth, fHeight, fBaseline;
|
||||
BFont fFieldFont;
|
||||
BFont fHintFont;
|
||||
float fHintHeight, fHintWidth, fHintBaseline;
|
||||
uint32 fShowHintX, fShowHintY;
|
||||
uint32 fLastHintValue;
|
||||
uint32 fLastField;
|
||||
uint32 fKeyboardX, fKeyboardY;
|
||||
uint32 fHintFlags;
|
||||
bool fShowKeyboardFocus;
|
||||
bool fShowCursor;
|
||||
bool fEditable;
|
||||
};
|
||||
|
||||
static const uint32 kMsgSudokuSolved = 'susl';
|
||||
static const uint32 kMsgSolveSudoku = 'slvs';
|
||||
static const uint32 kMsgSolveSingle = 'slsg';
|
||||
|
||||
#endif // SUDOKU_VIEW_H
|
459
src/apps/sudoku/SudokuWindow.cpp
Normal file
459
src/apps/sudoku/SudokuWindow.cpp
Normal file
@ -0,0 +1,459 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "SudokuWindow.h"
|
||||
|
||||
#include "ProgressWindow.h"
|
||||
#include "Sudoku.h"
|
||||
#include "SudokuField.h"
|
||||
#include "SudokuGenerator.h"
|
||||
#include "SudokuView.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Alert.h>
|
||||
#include <Application.h>
|
||||
#include <File.h>
|
||||
#include <FilePanel.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <Menu.h>
|
||||
#include <MenuBar.h>
|
||||
#include <MenuItem.h>
|
||||
#include <Path.h>
|
||||
#include <Roster.h>
|
||||
|
||||
#include <be_apps/Tracker/RecentItems.h>
|
||||
|
||||
|
||||
const uint32 kMsgOpenFilePanel = 'opfp';
|
||||
const uint32 kMsgGenerateVeryEasySudoku = 'gnsv';
|
||||
const uint32 kMsgGenerateEasySudoku = 'gnse';
|
||||
const uint32 kMsgGenerateHardSudoku = 'gnsh';
|
||||
const uint32 kMsgAbortSudokuGenerator = 'asgn';
|
||||
const uint32 kMsgSudokuGenerated = 'sugn';
|
||||
const uint32 kMsgMarkInvalid = 'minv';
|
||||
const uint32 kMsgMarkValidHints = 'mvht';
|
||||
const uint32 kMsgNew = 'new ';
|
||||
const uint32 kMsgStartAgain = 'stag';
|
||||
const uint32 kMsgExportAsText = 'extx';
|
||||
|
||||
|
||||
class GenerateSudoku {
|
||||
public:
|
||||
GenerateSudoku(SudokuField& target, int32 level, BMessenger progress,
|
||||
BMessenger target);
|
||||
~GenerateSudoku();
|
||||
|
||||
void Abort();
|
||||
|
||||
private:
|
||||
void _Generate();
|
||||
static status_t _GenerateThread(void* self);
|
||||
|
||||
SudokuField fField;
|
||||
BMessenger fTarget;
|
||||
BMessenger fProgress;
|
||||
thread_id fThread;
|
||||
int32 fLevel;
|
||||
bool fQuit;
|
||||
};
|
||||
|
||||
|
||||
GenerateSudoku::GenerateSudoku(SudokuField& field, int32 level,
|
||||
BMessenger progress, BMessenger target)
|
||||
:
|
||||
fField(field),
|
||||
fTarget(target),
|
||||
fProgress(progress),
|
||||
fLevel(level),
|
||||
fQuit(false)
|
||||
{
|
||||
fThread = spawn_thread(_GenerateThread, "sudoku generator",
|
||||
B_LOW_PRIORITY, this);
|
||||
if (fThread >= B_OK)
|
||||
resume_thread(fThread);
|
||||
else
|
||||
_Generate();
|
||||
}
|
||||
|
||||
|
||||
GenerateSudoku::~GenerateSudoku()
|
||||
{
|
||||
Abort();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GenerateSudoku::Abort()
|
||||
{
|
||||
fQuit = true;
|
||||
|
||||
status_t status;
|
||||
wait_for_thread(fThread, &status);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GenerateSudoku::_Generate()
|
||||
{
|
||||
SudokuGenerator generator;
|
||||
|
||||
bigtime_t start = system_time();
|
||||
generator.Generate(&fField, 40 - fLevel * 5, fProgress, &fQuit);
|
||||
printf("generated in %g msecs\n",
|
||||
(system_time() - start) / 1000.0);
|
||||
|
||||
BMessage done(kMsgSudokuGenerated);
|
||||
if (!fQuit) {
|
||||
BMessage field;
|
||||
if (fField.Archive(&field, true) == B_OK)
|
||||
done.AddMessage("field", &field);
|
||||
}
|
||||
|
||||
fTarget.SendMessage(&done);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ status_t
|
||||
GenerateSudoku::_GenerateThread(void* _self)
|
||||
{
|
||||
GenerateSudoku* self = (GenerateSudoku*)_self;
|
||||
self->_Generate();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
SudokuWindow::SudokuWindow()
|
||||
: BWindow(BRect(100, 100, 500, 520), "Sudoku", B_TITLED_WINDOW,
|
||||
B_ASYNCHRONOUS_CONTROLS),
|
||||
fGenerator(NULL)
|
||||
{
|
||||
BMessage settings;
|
||||
_LoadSettings(settings);
|
||||
|
||||
BRect frame;
|
||||
if (settings.FindRect("window frame", &frame) == B_OK) {
|
||||
MoveTo(frame.LeftTop());
|
||||
ResizeTo(frame.Width(), frame.Height());
|
||||
frame.OffsetTo(B_ORIGIN);
|
||||
} else
|
||||
frame = Bounds();
|
||||
|
||||
// create GUI
|
||||
|
||||
BMenuBar* menuBar = new BMenuBar(Bounds(), "menu");
|
||||
AddChild(menuBar);
|
||||
|
||||
frame.top = menuBar->Frame().bottom;
|
||||
|
||||
BView* top = new BView(frame, NULL, B_FOLLOW_ALL, B_WILL_DRAW);
|
||||
top->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
||||
AddChild(top);
|
||||
|
||||
fSudokuView = new SudokuView(top->Bounds().InsetByCopy(10, 10),
|
||||
"sudoku view", settings, B_FOLLOW_ALL);
|
||||
top->AddChild(fSudokuView);
|
||||
|
||||
// add menu
|
||||
|
||||
BMenu* menu = new BMenu("File");
|
||||
menu->AddItem(new BMenuItem("New", new BMessage(kMsgNew)));
|
||||
menu->AddItem(new BMenuItem("Start Again", new BMessage(kMsgStartAgain)));
|
||||
menu->AddSeparatorItem();
|
||||
BMenu* recentsMenu = BRecentFilesList::NewFileListMenu(
|
||||
"Open File" B_UTF8_ELLIPSIS, NULL, NULL, this, 10, false, NULL,
|
||||
kSignature);
|
||||
BMenuItem *item;
|
||||
menu->AddItem(item = new BMenuItem(recentsMenu,
|
||||
new BMessage(kMsgOpenFilePanel)));
|
||||
item->SetShortcut('O', B_COMMAND_KEY);
|
||||
BMenu* subMenu = new BMenu("Generate");
|
||||
subMenu->AddItem(new BMenuItem("Very Easy",
|
||||
new BMessage(kMsgGenerateVeryEasySudoku)));
|
||||
subMenu->AddItem(new BMenuItem("Easy",
|
||||
new BMessage(kMsgGenerateEasySudoku)));
|
||||
subMenu->AddItem(new BMenuItem("Hard",
|
||||
new BMessage(kMsgGenerateHardSudoku)));
|
||||
menu->AddItem(subMenu);
|
||||
|
||||
menu->AddSeparatorItem();
|
||||
|
||||
menu->AddItem(new BMenuItem("Export As Text" B_UTF8_ELLIPSIS,
|
||||
new BMessage(kMsgExportAsText)));
|
||||
|
||||
menu->AddSeparatorItem();
|
||||
|
||||
menu->AddItem(item = new BMenuItem("About Sudoku" B_UTF8_ELLIPSIS,
|
||||
new BMessage(B_ABOUT_REQUESTED)));
|
||||
menu->AddSeparatorItem();
|
||||
|
||||
menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED),
|
||||
'Q', B_COMMAND_KEY));
|
||||
menu->SetTargetForItems(this);
|
||||
item->SetTarget(be_app);
|
||||
menuBar->AddItem(menu);
|
||||
|
||||
menu = new BMenu("View");
|
||||
menu->AddItem(item = new BMenuItem("Mark Invalid Values",
|
||||
new BMessage(kMsgMarkInvalid)));
|
||||
if (fSudokuView->HintFlags() & kMarkInvalid)
|
||||
item->SetMarked(true);
|
||||
menu->AddItem(item = new BMenuItem("Mark Valid Hints",
|
||||
new BMessage(kMsgMarkValidHints)));
|
||||
if (fSudokuView->HintFlags() & kMarkValidHints)
|
||||
item->SetMarked(true);
|
||||
menu->SetTargetForItems(this);
|
||||
menuBar->AddItem(menu);
|
||||
|
||||
menu = new BMenu("Help");
|
||||
menu->AddItem(new BMenuItem("Solve", new BMessage(kMsgSolveSudoku)));
|
||||
menu->AddItem(new BMenuItem("Solve Single Field",
|
||||
new BMessage(kMsgSolveSingle)));
|
||||
menu->SetTargetForItems(fSudokuView);
|
||||
menuBar->AddItem(menu);
|
||||
|
||||
fOpenPanel = new BFilePanel(B_OPEN_PANEL);
|
||||
fOpenPanel->SetTarget(this);
|
||||
fSavePanel = new BFilePanel(B_SAVE_PANEL);
|
||||
fSavePanel->SetTarget(this);
|
||||
|
||||
fProgressWindow = new ProgressWindow(this,
|
||||
new BMessage(kMsgAbortSudokuGenerator));
|
||||
}
|
||||
|
||||
|
||||
SudokuWindow::~SudokuWindow()
|
||||
{
|
||||
delete fOpenPanel;
|
||||
delete fSavePanel;
|
||||
delete fGenerator;
|
||||
|
||||
if (fProgressWindow->Lock())
|
||||
fProgressWindow->Quit();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SudokuWindow::_OpenSettings(BFile& file, uint32 mode)
|
||||
{
|
||||
BPath path;
|
||||
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
|
||||
return B_ERROR;
|
||||
|
||||
path.Append("pinc.Sudoku settings");
|
||||
|
||||
return file.SetTo(path.Path(), mode);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SudokuWindow::_LoadSettings(BMessage& settings)
|
||||
{
|
||||
BFile file;
|
||||
status_t status = _OpenSettings(file, B_READ_ONLY);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
return settings.Unflatten(&file);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SudokuWindow::_SaveSettings()
|
||||
{
|
||||
BFile file;
|
||||
status_t status = _OpenSettings(file, B_WRITE_ONLY | B_CREATE_FILE
|
||||
| B_ERASE_FILE);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
BMessage settings('sudo');
|
||||
status = settings.AddRect("window frame", Frame());
|
||||
if (status == B_OK)
|
||||
status = fSudokuView->SaveState(settings);
|
||||
if (status == B_OK)
|
||||
status = settings.Flatten(&file);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuWindow::_MessageDropped(BMessage* message)
|
||||
{
|
||||
status_t status = B_MESSAGE_NOT_UNDERSTOOD;
|
||||
bool hasRef = false;
|
||||
|
||||
entry_ref ref;
|
||||
if (message->FindRef("refs", &ref) != B_OK) {
|
||||
const void* data;
|
||||
ssize_t size;
|
||||
if (message->FindData("text/plain", B_MIME_TYPE, &data,
|
||||
&size) == B_OK) {
|
||||
status = fSudokuView->SetTo((const char*)data);
|
||||
} else
|
||||
return;
|
||||
} else {
|
||||
status = fSudokuView->SetTo(ref);
|
||||
if (status == B_OK)
|
||||
be_roster->AddToRecentDocuments(&ref, kSignature);
|
||||
|
||||
BEntry entry(&ref);
|
||||
entry_ref parent;
|
||||
if (entry.GetParent(&entry) == B_OK
|
||||
&& entry.GetRef(&parent) == B_OK)
|
||||
fSavePanel->SetPanelDirectory(&parent);
|
||||
|
||||
hasRef = true;
|
||||
}
|
||||
|
||||
if (status < B_OK) {
|
||||
char buffer[1024];
|
||||
if (hasRef) {
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Could not open \"%s\":\n"
|
||||
"%s", ref.name, strerror(status));
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "Could not set Sudoku:\n%s",
|
||||
strerror(status));
|
||||
}
|
||||
|
||||
(new BAlert("Sudoku request",
|
||||
buffer, "Ok", NULL, NULL,
|
||||
B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuWindow::_Generate(int32 level)
|
||||
{
|
||||
if (fGenerator != NULL)
|
||||
delete fGenerator;
|
||||
|
||||
fSudokuView->SetEditable(false);
|
||||
fProgressWindow->Start();
|
||||
fGenerator = new GenerateSudoku(*fSudokuView->Field(), level,
|
||||
fProgressWindow, this);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SudokuWindow::MessageReceived(BMessage* message)
|
||||
{
|
||||
if (message->WasDropped()) {
|
||||
_MessageDropped(message);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message->what) {
|
||||
case kMsgOpenFilePanel:
|
||||
fOpenPanel->Show();
|
||||
break;
|
||||
|
||||
case B_REFS_RECEIVED:
|
||||
case B_SIMPLE_DATA:
|
||||
_MessageDropped(message);
|
||||
break;
|
||||
|
||||
case kMsgGenerateVeryEasySudoku:
|
||||
_Generate(0);
|
||||
break;
|
||||
case kMsgGenerateEasySudoku:
|
||||
_Generate(2);
|
||||
break;
|
||||
case kMsgGenerateHardSudoku:
|
||||
_Generate(4);
|
||||
break;
|
||||
case kMsgAbortSudokuGenerator:
|
||||
if (fGenerator != NULL)
|
||||
fGenerator->Abort();
|
||||
break;
|
||||
case kMsgSudokuGenerated:
|
||||
{
|
||||
BMessage archive;
|
||||
if (message->FindMessage("field", &archive) == B_OK) {
|
||||
SudokuField* field = new SudokuField(&archive);
|
||||
fSudokuView->SetTo(field);
|
||||
}
|
||||
fSudokuView->SetEditable(true);
|
||||
fProgressWindow->Stop();
|
||||
|
||||
delete fGenerator;
|
||||
fGenerator = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
case kMsgExportAsText:
|
||||
fSavePanel->Show();
|
||||
break;
|
||||
|
||||
case B_SAVE_REQUESTED:
|
||||
{
|
||||
entry_ref directoryRef;
|
||||
const char* name;
|
||||
if (message->FindRef("directory", &directoryRef) != B_OK
|
||||
|| message->FindString("name", &name) != B_OK)
|
||||
break;
|
||||
|
||||
BDirectory directory(&directoryRef);
|
||||
BEntry entry(&directory, name);
|
||||
|
||||
entry_ref ref;
|
||||
if (entry.GetRef(&ref) == B_OK)
|
||||
fSudokuView->SaveTo(ref, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case kMsgNew:
|
||||
fSudokuView->ClearAll();
|
||||
break;
|
||||
|
||||
case kMsgStartAgain:
|
||||
fSudokuView->ClearChanged();
|
||||
break;
|
||||
|
||||
case kMsgMarkInvalid:
|
||||
case kMsgMarkValidHints:
|
||||
{
|
||||
BMenuItem* item;
|
||||
if (message->FindPointer("source", (void**)&item) != B_OK)
|
||||
return;
|
||||
|
||||
uint32 flag = message->what == kMsgMarkInvalid
|
||||
? kMarkInvalid : kMarkValidHints;
|
||||
|
||||
item->SetMarked(!item->IsMarked());
|
||||
if (item->IsMarked())
|
||||
fSudokuView->SetHintFlags(fSudokuView->HintFlags() | flag);
|
||||
else
|
||||
fSudokuView->SetHintFlags(fSudokuView->HintFlags() & ~flag);
|
||||
break;
|
||||
}
|
||||
|
||||
case kMsgSudokuSolved:
|
||||
(new BAlert("Sudoku request",
|
||||
"Sudoku solved - congratulations!", "Ok", NULL, NULL,
|
||||
B_WIDTH_AS_USUAL, B_IDEA_ALERT))->Go();
|
||||
break;
|
||||
|
||||
default:
|
||||
BWindow::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SudokuWindow::QuitRequested()
|
||||
{
|
||||
_SaveSettings();
|
||||
be_app->PostMessage(B_QUIT_REQUESTED);
|
||||
return true;
|
||||
}
|
41
src/apps/sudoku/SudokuWindow.h
Normal file
41
src/apps/sudoku/SudokuWindow.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef SUDOKU_WINDOW_H
|
||||
#define SUDOKU_WINDOW_H
|
||||
|
||||
|
||||
#include <Window.h>
|
||||
|
||||
class BFile;
|
||||
class BFilePanel;
|
||||
class GenerateSudoku;
|
||||
class ProgressWindow;
|
||||
class SudokuView;
|
||||
|
||||
|
||||
class SudokuWindow : public BWindow {
|
||||
public:
|
||||
SudokuWindow();
|
||||
virtual ~SudokuWindow();
|
||||
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
virtual bool QuitRequested();
|
||||
|
||||
private:
|
||||
status_t _OpenSettings(BFile& file, uint32 mode);
|
||||
status_t _LoadSettings(BMessage& settings);
|
||||
status_t _SaveSettings();
|
||||
|
||||
void _MessageDropped(BMessage *message);
|
||||
void _Generate(int32 level);
|
||||
|
||||
BFilePanel* fOpenPanel;
|
||||
BFilePanel* fSavePanel;
|
||||
ProgressWindow* fProgressWindow;
|
||||
SudokuView* fSudokuView;
|
||||
GenerateSudoku* fGenerator;
|
||||
};
|
||||
|
||||
#endif // SUDOKU_WINDOW_H
|
Loading…
Reference in New Issue
Block a user