summaryrefslogtreecommitdiffstats
path: root/examples/scxml/sudoku/doc/src/sudoku.qdoc
blob: 45a2967843b78d0c9b4cdee56f42e67bac2b076b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \example sudoku
    \title SCXML Sudoku
    \examplecategory {Data Processing & I/O}
    \ingroup examples-qtscxml
    \meta tag {state machine}

    \brief Demonstrates the use of separate javascript file with SCXML.

    \include examples-run.qdocinc

    \section1 Sudoku Features

    \image sudoku.png Screenshot of the Sudoku example

    Our sudoku contains the following features:
    \list
    \li Initially and when the game ends, the sudoku
        enters the \c idle state. In that state the players
        can see if their last game finished successfully or not.
        The state machine is then in one of two child states
        of the \c idle state: \c solved or \c unsolved,
        respectively. In the \c idle state the players
        can also choose the sudoku grid they would like to solve.
        The grid is disabled and the user interaction
        is ignored.
    \li After players click the \uicontrol Start button, the sudoku
        enters the \c playing state and is ready for the user
        interaction on the board.
    \li When the game is in the \c playing state and the players
        click the \uicontrol Stop button, the game ends
        and enters the \c unsolved child state of the \c idle state.
        If the players have solved the current puzzle successfully,
        the game automatically ends and enters the \c solved child state
        of the \c idle state indicating success.
    \li The board consist of 81 buttons, laid out in a 9x9 grid.
        The buttons with initial values given remain disabled
        during the game. The players can only interact
        with buttons initially empty. Each
        click on the button increases its value by one.
    \li During the game the \uicontrol Undo button
        is available for the players' convenience.
    \endlist

    \section1 SCXML Part: Internal Logic Description

    The \e sudoku.scxml file describes the internal structure of
    the states the sudoku game can be in, defines the transitions
    between states, and triggers the appropriate script
    functions when the transitions take place. It also
    communicates with the GUI part by sending events and listening
    to the upcoming events and reacting to them.

    We use the ECMAScript data model:

    \quotefromfile sudoku/sudoku.scxml
    \skipto scxml
    \printuntil ecmascript

    We declare the following variables:

    \printuntil </datamodel>

    \table
    \header
        \li Variable
        \li Description
    \row
        \li \c initState
        \li Holds the initial state of the current game. It is a
            two-dimensional array of 9x9 cells that contain initial sudoku
            numbers. The value of zero means the cell is initially empty.
    \row
        \li \c currentState
        \li Holds the current state of the game being played. It is
            similar to the \c initState variable and initially
            contains the same content. However, when the players start
            entering the numbers into the empty cells, this variable
            is being updated accordingly, while the \c initState variable
            remains unchanged.
    \row
        \li \c undoStack
        \li Holds the history of players' moves. It is a list of
            the cells' coordinates that were touched last. Each
            new modification during a game adds a pair of
            x and y coordinates to that list.
    \endtable

    The variables above are shared with the script helper functions
    defined in the \c sudoku.js file:

    \printuntil sudoku.js

    We call some of the functions defined there
    when taking transitions or in reaction to the
    events sent by the GUI.

    All the possible states mentioned before are defined in a
    root state \c game.

    \printuntil idle
    \dots 12
    \skipto id="unsolved"
    \printuntil playing
    \dots 12
    \skipto /^\ {8}<\//
    \printline state
    \dots 8
    \skipto /^\ {4}<\//
    \printline state

    When the sudoku example is started, the state
    machine enters the \c game state and stays in this
    state until the application exits. When entering this
    state, we raise internally the \c restart event.
    This event is also being raised whenever the players
    change the current sudoku grid or when they start
    the game by pressing the \uicontrol Start button.
    We do not want to send it when they have finished
    the current game because we still want to show the
    filled grid from the last game play. So, this event
    is being raised from three different contexts
    and is captured internally once in a targetless
    transition of the \c game state:

    \quotefromfile sudoku/sudoku.scxml
    \skipto <transition event="restart">
    \printuntil </transition>

    When we catch the \c restart event, we call
    a helper \c restart() script method, defined in the \c sudoku.js
    file and raise internally an additional \c update event.

    \quotefromfile sudoku/sudoku.js
    \skipto restart
    \printuntil }

    The \c restart() function assigns the \c initState into the \c currentState
    variable and clears the \c undoStack variable.

    The \c update event is raised internally whenever we want to notify the GUI
    that the grid contents have been changed and that
    the GUI should update itself according to the passed values. This event
    is caught in another targetless transition of the \c game state:

    \quotefromfile sudoku/sudoku.scxml
    \skipto <transition event="update">
    \printuntil </transition>

    We send the external event \c updateGUI,
    which is being intercepted in the \l {cpp}{C++ code}.
    The \c updateGUI event is equipped
    with additional data, specified inside \c <param>
    elements. We pass two parameters, which
    are accessible externally through the
    \c currentState and \c initState names.
    The actual values passed for them
    equal the datamodel's \c currentState
    and \c initState variables, respectively, which
    are specified by the \c expr attributes.

    \quotefromfile sudoku/sudoku.scxml
    \skipto idle
    \printuntil </state>

    When in \c idle state, we react to two
    events, which may be sent by the GUI part:
    \c start and \c setup. Whenever we receive the
    \c start event, we just transition to the \c playing
    state. When we receive the \c setup event, we
    expect that the GUI part has sent us the new grid
    to be solved. The grid's new initial state is expected
    to be passed through the \c initState field of
    \c _event.data. We assign the passed value to the
    \c initState variable defined in our datamodel and
    restart the grid's contents.

    \skipto playing
    \printuntil </transition>
    \dots 12
    \skipto </state>
    \printline </state>

    Whenever we enter the \c playing state, we reset
    the grid's contents since we could have been still
    showing the contents from the previous game play.
    In the \c playing state we react to possible
    events sent from the GUI: \c tap, \c undo, and \c stop.

    The \c tap event is sent when the players
    press one of the enabled sudoku cells.
    This event is expected to contain additional data
    specifying the cell's coordinates, which are passed
    through the \c x and \c y fields of \c _event.data.
    First, we check if the passed coordinates are valid
    by invoking the \c isValidPosition() script function:

    \quotefromfile sudoku/sudoku.js
    \skipto isValidPosition
    \printuntil }

    We ensure the coordinates are neither negative nor bigger than our grid.
    In addition, we check if the coordinates point to an
    initially empty cell, since we can not modify
    the cells initially given by the grid description.

    When we have ensured the passed coordinates are correct,
    we call \c calculateCurrentState() script function:

    \quotefromfile sudoku/sudoku.js
    \skipto calculateCurrentState
    \printuntil }

    This function increments the value of the passed
    grid's cell and adds the new move to the
    undo stack history.

    Right after the \c calculateCurrentState() function
    finishes its execution, we check whether the grid is
    already solved by calling the \c isSolved() script function:

    \quotefromfile sudoku/sudoku.js
    \skipto isOK
    \printuntil return true
    \printuntil }
    \skipto isSolved
    \printuntil return true
    \printuntil }

    The \c isSolved() function returns \c true if the
    grid is properly solved. Since we need to check
    each row, each column, and each 3x3 square, we define
    the \c isOK() helper function. This function takes
    the list of numbers and returns \c true if the passed list
    contains unique numbers and no number equals zero, meaning
    there is no empty cell. The main loop of the \c isSolved() is invoked
    nine times. In every iteration, we construct three lists of numbers
    representing a row, a column, and a square of the grid and call \c isOK()
    for them. When all 27 lists are OK, the grid is solved properly
    and we return \c true.

    Coming back to our SCXML file, in case \c isSolved()
    returns \c true, we raise the \c solved event internally.
    The last instruction in case of a proper move is to raise
    the \c update event, since we need to notify the GUI
    about the grid's change.

    \quotefromfile sudoku/sudoku.scxml
    \skipto id="playing"
    \printline playing
    \dots 12
    \skipto undo
    \printuntil </state>

    When in the \c playing state, we also react to the \c undo
    event sent from the GUI. In this case, we call the \c undo()
    script function and notify the GUI about the need of an update.

    \quotefromfile sudoku/sudoku.js
    \skipto function undo
    \printuntil }

    The \c undo() function removes the last move from
    the history, if there was any, and decrements the current value
    for the cell described by the coordinates taken from this move.

    The \c playing state is also ready for the \c stop
    event sent by the GUI when the players press the \uicontrol Stop
    button. In this case, we simply activate the \c idle state.

    In addition, we intercept the \c solved event
    sent internally and activate the \c solved state in this case.

    \target cpp
    \section1 C++ Part: Constructing the GUI

    The C++ part of the application consists of a
    \c MainWindow class which constructs the GUI and glues it with the SCXML part.
    The class is declared in \e mainwindow.h.

    \quotefromfile sudoku/mainwindow.h
    \skipto MainWindow
    \printuntil };

    The \c MainWindow class holds the pointer to the
    \c {QScxmlStateMachine *m_machine}, which is the state machine
    class automatically generated by Qt out of the \c sudoku.scxml file.
    It also holds the pointers to some GUI elements.

    \quotefromfile sudoku/mainwindow.cpp
    \skipto MainWindow
    \printuntil {

    The constructor of the \c MainWindow class
    instantiates the GUI part of the application
    and stores the pointer to the passed state machine.
    It also initializes the GUI part and glues the
    GUI part to the state machine by connecting
    their communication interfaces together.

    \skipto clicked
    \printuntil });

    First, we create 81 buttons and connect
    their \c clicked signal to a lambda expression
    that submits the \c tap event to the state machine
    passing the button's coordinates.

    Later, we add some horizontal and vertical lines
    to the grid in order to group buttons in 3x3 boxes.

    \skipto clicked
    \printuntil });

    We create the \uicontrol {Start / Stop} button and
    connect its clicked signal to a lambda expression
    which submits the \c stop or \c start event
    depending on whether the machine is in \c playing state
    or not, respectively.

    We create a label informing whether the grid is solved
    or not, and an \uicontrol Undo button, which submits the
    \c undo event whenever it is clicked.

    \skipto clicked
    \printuntil });

    Then we create a combobox that is filled with
    grid names to be solved. These grids are
    read from the \c :/data directory of the application
    compiled-in resources.

    \skipto currentIndexChanged
    \printuntil setInitialValues

    Whenever the players change the grid in the combobox,
    we read the grid contents storing it in the
    variant map under the \c initValues key as a
    list of lists of int variants and we submit the
    \c setup event to the state machine passing
    the grid's contents. Initially, we read the first
    available grid from the list and pass it directly
    to the sudoku state machine as the initial grid.

    \skipto connectToState
    \printline playing
    \dots 8
    \skipto });
    \printuntil connectToEvent
    \dots 8
    \skipto });
    \printline });

    Later, we connect to the signals that are being sent
    whenever the machine enters or leaves the \c playing
    or \c solved states, and we update some GUI parts accordingly.
    We also connect to the state machine's \c updateGUI event
    and update all the buttons' values according to the passed cells' states.

    \quotefromfile sudoku/main.cpp
    \skipto #include
    \printuntil /\}$/

    In the \c main() function in the \c main.cpp file, we instantiate the
    \c app application object, \c Sudoku state machine,
    and \c MainWindow GUI class. We start the state machine,
    show the main window, and execute the application.
*/