~sil/dropping-letters/add-vcs

« back to all changes in this revision

Viewing changes to dropping-letters.qml

  • Committer: Ken VanDine
  • Date: 2013-05-01 05:01:59 UTC
  • mto: This revision was merged to the branch mainline in revision 23.
  • Revision ID: ken.vandine@canonical.com-20130501050159-7odmxs9ma3zddh9k
merged packaging

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
import QtQuick 2.0
2
 
import QtMultimedia 5.0
3
 
import "binarydict.js" as Wordlist
4
 
import QtQuick.Particles 2.0
5
 
import QtQuick.LocalStorage 2.0
6
 
 
7
 
Flipable {
8
 
    id: flipable
9
 
    property bool flipped: false
10
 
    property variant db: null
11
 
    width: 48 * 7 + 2 * 7 + 2
12
 
    height: 48 * 10 + 2 * 10 + 50
13
 
    transform: Rotation {
14
 
        id: rotation
15
 
        origin.x: flipable.width/2
16
 
        origin.y: flipable.height/2
17
 
        axis.x: 0; axis.y: 1; axis.z: 0     // set axis.y to 1 to rotate around y-axis
18
 
        angle: 0    // the default angle
19
 
    }
20
 
 
21
 
    Component.onCompleted: {
22
 
        var db = LocalStorage.openDatabaseSync("dropping-letters", "1.0", "Dropping Letters", 1000);
23
 
        db.transaction(function(tx) {
24
 
            // Create the database if it doesn't already exist
25
 
            tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(score INT, time TEXT)');
26
 
            var res = tx.executeSql('SELECT score from Scores order by score DESC LIMIT 1');
27
 
            if (res.rows.length === 0) {
28
 
                bestscore.updateScore(0);
29
 
            } else {
30
 
                bestscore.updateScore(res.rows.item(0).score);
31
 
            }
32
 
        });
33
 
    }
34
 
 
35
 
    states: [
36
 
        State {
37
 
            name: "back"
38
 
            PropertyChanges { target: rotation; angle: 180 }
39
 
            when: flipable.flipped
40
 
        },
41
 
        State {
42
 
            name: "front"
43
 
            when: !flipable.flipped
44
 
        }
45
 
    ]
46
 
 
47
 
    transitions: [
48
 
        Transition {
49
 
            NumberAnimation { target: rotation; property: "angle"; duration: 200 }
50
 
        }
51
 
    ]
52
 
 
53
 
    front: Rectangle {
54
 
        id: entry
55
 
        color: "#58585A"
56
 
        width: 48 * 7 + 2 * 7 + 2
57
 
        height: 48 * 10 + 2 * 10 + 50
58
 
        Column {
59
 
            id: logo
60
 
            anchors.centerIn: parent
61
 
            spacing: 2
62
 
            Repeater {
63
 
                model: ["DROP", "PING", "LETT", "ERS "]
64
 
                Row {
65
 
                    spacing: 2
66
 
                    property int lineIndex: index
67
 
                    property string modelString: modelData
68
 
                    Repeater {
69
 
                        model: modelData.split("")
70
 
                        Rectangle {
71
 
                            id: titleletter
72
 
                            width: 50
73
 
                            height: 50
74
 
                            color: "#939598"
75
 
                            radius: 3
76
 
                            Text {
77
 
                                text: modelData
78
 
                                anchors.centerIn: parent
79
 
                                font.pixelSize: titleletter.height * 0.6
80
 
                                color: "#ffffff"
81
 
                                font.family: "Helvetica"
82
 
                            }
83
 
                            states: [
84
 
                                State { name: "showing"; when: flipable.state == "front"
85
 
                                    PropertyChanges { target: titleletter; y: 0 }
86
 
                                },
87
 
                                State { name: "notshowing"; when: flipable.state != "front"
88
 
                                    PropertyChanges { target: titleletter; y: -500 }
89
 
                                }
90
 
                            ]
91
 
                            Behavior on y {
92
 
                                SequentialAnimation {
93
 
                                    PauseAnimation {
94
 
                                        duration: (35 * parent.modelString.length * (4-parent.lineIndex)) + (
95
 
                                            35 * index) }
96
 
                                    NumberAnimation { easing.type: Easing.OutQuart }
97
 
                                }
98
 
                            }
99
 
                        }
100
 
                    }
101
 
                }
102
 
            }
103
 
        }
104
 
        Text {
105
 
            id: playbutton
106
 
            text: "play"
107
 
            anchors.top: logo.bottom
108
 
            anchors.topMargin: 15
109
 
            anchors.horizontalCenter: logo.horizontalCenter
110
 
            font.pixelSize: 30
111
 
            color: "white"
112
 
        }
113
 
        Text {
114
 
            id: bestscore
115
 
            property int bestsofar: 0
116
 
            text: "..."
117
 
            anchors.top: playbutton.bottom
118
 
            anchors.topMargin: 15
119
 
            anchors.horizontalCenter: logo.horizontalCenter
120
 
            font.pixelSize: 30
121
 
            color: "white"
122
 
            function updateScore(score) {
123
 
                if (score >= bestscore.bestsofar) {
124
 
                    bestscore.text = "Best score: " + score;
125
 
                    bestscore.bestsofar = score;
126
 
                }
127
 
            }
128
 
        }
129
 
        MouseArea {
130
 
            anchors.fill: parent
131
 
            enabled: !flipable.flipped
132
 
            onClicked: {
133
 
                lm.clear();
134
 
                for (var i=0; i<7; i++) {
135
 
                    lm.append({'letters': [] });
136
 
                }
137
 
                flipable.flipped = true
138
 
            }
139
 
        }
140
 
    }
141
 
 
142
 
    back: Rectangle {
143
 
        id: main
144
 
        color: "#58585A"
145
 
        width: 48 * 7 + 2 * 7 + 2
146
 
        height: 48 * 10 + 2 * 10 + 50
147
 
        property var selectedItems: []
148
 
        /*
149
 
        obtained with:
150
 
        import re; fp=open('/usr/share/dict/words');
151
 
        shorts=[re.sub('[^a-zA-Z]','',x.lower().strip()) for x in fp if len(x)<7];
152
 
        from collections import Counter; c=Counter(''.join(shorts)); least=c.most_common()[-1][1];
153
 
        print dict([(x.upper(),c[x]/least) for x in c])
154
 
        */
155
 
        property variant letterFreqs: {
156
 
            'A': 66, 'C': 22, 'B': 19, 'E': 72, 'D': 28, 'G': 18, 'F': 12, 'I': 41, 'H': 20,
157
 
            'K': 14, 'J': 4, 'M': 22, 'L': 40, 'O': 48, 'N': 35, 'Q': 1, 'P': 22, 'S': 75,
158
 
            'R': 43, 'U': 26, 'T': 37, 'W': 13, 'V': 7, 'Y': 19, 'X': 3, 'Z': 4
159
 
        }
160
 
        property variant letterScores: {
161
 
            "A":1,"B":3,"C":3,"D":2,"E":1,"F":4,"G":2,"H":4,"I":1,"J":8,"K":5,"L":1,
162
 
            "M":3,"N":1,"O":1,"P":3,"Q":10,"R":1,"S":1,"T":1,"U":1,"V":4,"W":4,"X":8,
163
 
            "Y":4,"Z":10
164
 
        }
165
 
        property int score: 0
166
 
 
167
 
        function getRandomWeightedLetter() {
168
 
            // could work out sumOfWeights once, but this is easier to understand
169
 
            var sumOfWeights = 0;
170
 
            for (var k in main.letterFreqs) { sumOfWeights += main.letterFreqs[k]; }
171
 
            var selection = Math.floor(Math.random() * sumOfWeights);
172
 
            for (var k in main.letterFreqs) {
173
 
                if (selection < main.letterFreqs[k]) return k;
174
 
                selection -= main.letterFreqs[k];
175
 
            }
176
 
        }
177
 
 
178
 
        Audio {
179
 
            id: music
180
 
            source: "Easy Lemon 60 second.ogg"
181
 
            autoPlay: true
182
 
            onStopped: music.play()
183
 
            muted: !volume.audible
184
 
        }
185
 
 
186
 
        Audio {
187
 
            id: click
188
 
            source: "407__tictacshutup__click-1-off-click.ogg"
189
 
            autoLoad: true
190
 
            muted: !volume.audible
191
 
        }
192
 
 
193
 
        Audio {
194
 
            id: success
195
 
            source: "80921__justinbw__buttonchime02up.ogg"
196
 
            autoLoad: true
197
 
            muted: !volume.audible
198
 
        }
199
 
 
200
 
        Audio {
201
 
            id: failure
202
 
            source: "106727__kantouth__cartoon-bing-low.ogg"
203
 
            autoLoad: true
204
 
            muted: !volume.audible
205
 
        }
206
 
 
207
 
        Audio {
208
 
            id: gameover
209
 
            source: "45137__dj-chronos__dark-church-bell.ogg"
210
 
            autoLoad: true
211
 
            onStopped: {
212
 
                flipable.flipped = false
213
 
            }
214
 
            muted: !volume.audible
215
 
        }
216
 
 
217
 
 
218
 
        Rectangle {
219
 
            id: volume
220
 
            width: 48
221
 
            height: 48
222
 
            color: "#a4a4a4"
223
 
            anchors.left: main.left
224
 
            anchors.top: main.top
225
 
            z: 2
226
 
            clip: true
227
 
            property int frame: 0
228
 
            property bool reverse: false
229
 
            property bool audible: true
230
 
            Image {
231
 
                id: image
232
 
                source: "volume-sprite.png"
233
 
                x: -parent.frame * parent.width
234
 
            }
235
 
            Timer {
236
 
                id: volumetimer
237
 
                running: false
238
 
                interval: 10
239
 
                repeat: true
240
 
                onTriggered: {
241
 
                    if (parent.reverse) {
242
 
                        parent.frame -= 1;
243
 
                        if (parent.frame == 0) volumetimer.running = false;
244
 
                    } else {
245
 
                        parent.frame += 1;
246
 
                        if (parent.frame == 5) volumetimer.running = false;
247
 
                    }
248
 
                }
249
 
            }
250
 
            MouseArea {
251
 
                anchors.fill: parent
252
 
                onClicked: {
253
 
                    if (!parent.audible) {
254
 
                        parent.frame = 5;
255
 
                        parent.reverse = true;
256
 
                        volumetimer.running = true;
257
 
                        parent.audible = true;
258
 
                    } else {
259
 
                        parent.frame = 0;
260
 
                        parent.reverse = false;
261
 
                        volumetimer.running = true;
262
 
                        parent.audible = false;
263
 
                    }
264
 
                }
265
 
            }
266
 
        }
267
 
 
268
 
        Text {
269
 
            id: scoredisplay
270
 
            anchors.centerIn: parent
271
 
            z: 3
272
 
            font.pixelSize: main.height
273
 
            text: "200"
274
 
            color: "red"
275
 
            opacity: 0
276
 
        }
277
 
 
278
 
        Rectangle {
279
 
            id: mainscore
280
 
            anchors.right: main.right
281
 
            anchors.top: main.top
282
 
            width: 50
283
 
            height: 48
284
 
            z: 2
285
 
            color: "#a4a4a4"
286
 
            Text {
287
 
                anchors.centerIn: parent
288
 
                font.pixelSize: 50/3
289
 
                text: "" + main.score
290
 
                color: "#222"
291
 
            }
292
 
        }
293
 
 
294
 
        ParallelAnimation {
295
 
            id: showscoredisplay
296
 
            NumberAnimation {
297
 
                property: "scale"
298
 
                from: 0.01
299
 
                to: 1.0
300
 
                duration: 400
301
 
                target: scoredisplay
302
 
            }
303
 
            SequentialAnimation {
304
 
                NumberAnimation {
305
 
                    property: "opacity"
306
 
                    from: 0
307
 
                    to: 1.0
308
 
                    duration: 20
309
 
                    target: scoredisplay
310
 
                }
311
 
                NumberAnimation {
312
 
                    property: "opacity"
313
 
                    from: 1.0
314
 
                    to: 0
315
 
                    duration: 380
316
 
                    target: scoredisplay
317
 
                }
318
 
            }
319
 
        }
320
 
 
321
 
 
322
 
 
323
 
        Timer {
324
 
            id: droptimer
325
 
            repeat: true
326
 
            running: flipable.flipped
327
 
            interval: 2000
328
 
            triggeredOnStart: true
329
 
            onTriggered: {
330
 
                var idx = Math.round(Math.random() * (lm.count - 1));
331
 
                lm.get(idx).letters.append({ letter: main.getRandomWeightedLetter() });
332
 
                if (lm.get(idx).letters.count >= 10) {
333
 
                    droptimer.stop();
334
 
                    gameover.play();
335
 
                    var db = LocalStorage.openDatabaseSync("dropping-letters", "1.0", "Dropping Letters", 1000);
336
 
                    db.transaction(function(tx) {
337
 
                        tx.executeSql("insert into Scores values (?,?)", [main.score, 0]);
338
 
                        bestscore.updateScore(main.score);
339
 
                    });
340
 
                }
341
 
            }
342
 
        }
343
 
 
344
 
        ListModel {
345
 
            id: lm
346
 
        }
347
 
 
348
 
        Rectangle {
349
 
            anchors.top: main.top
350
 
            anchors.left: main.left
351
 
            anchors.right: playarea.right
352
 
            anchors.bottom: playarea.top
353
 
            anchors.rightMargin: 50
354
 
            anchors.leftMargin: 50
355
 
            anchors.bottomMargin: 2
356
 
            z: 2
357
 
            color: accum.text == "" ? "#a4a4a4" : (accum.isValid ? "green" : "red")
358
 
            Text {
359
 
                id: accum
360
 
                anchors.centerIn: parent
361
 
                text: ""
362
 
                color: "white"
363
 
                font.pixelSize: parent.height * 0.7
364
 
                font.family: "Helvetica"
365
 
                property bool isValid: false
366
 
                function findBinaryWord( word ) {
367
 
                    var bd = Wordlist.wordlist;
368
 
                    var l = word.length;
369
 
                    // Don't search if there's nothing to look through
370
 
                    if ( !bd[l] ) {
371
 
                        return false;
372
 
                    }
373
 
                    // Get the number of words in the dictionary bin
374
 
                    var words = bd[l].length / l,
375
 
                    // The low point from where we're starting the binary search
376
 
                    low = 0,
377
 
                    // The max high point
378
 
                    high = words - 1,
379
 
                    // And the precise middle of the search
380
 
                    mid = Math.floor( words / 2 );
381
 
                    // We continue to look until we reach a final word
382
 
                    while ( high >= low ) {
383
 
                        // Grab the word at our current position
384
 
                        var found = bd[l].substr( l * mid, l );
385
 
                        // If we've found the word, stop now
386
 
                        if ( word === found ) {
387
 
                            return true;
388
 
                        }
389
 
                        // Otherwise, compare
390
 
                        // If we're too high, move lower
391
 
                        if ( word < found ) {
392
 
                            high = mid - 1;
393
 
                        // If we're too low, go higher
394
 
                        } else {
395
 
                            low = mid + 1;
396
 
                        }
397
 
                        // And find the new search point
398
 
                        mid = Math.floor( (low + high) / 2 );
399
 
                    }
400
 
                    // Nothing was found
401
 
                    return false;
402
 
                }
403
 
                onTextChanged: {
404
 
                    accum.isValid = findBinaryWord(accum.text);
405
 
                }
406
 
            }
407
 
            MouseArea {
408
 
                anchors.fill: parent
409
 
                onClicked: {
410
 
                    if (accum.isValid) {
411
 
                        success.play();
412
 
                        var thisscore = 0, wordlength = accum.text.length;
413
 
                        accum.text = "";
414
 
 
415
 
                        // tell the boxes to destroy themselves
416
 
                        main.selectedItems.forEach(function(b) {
417
 
                            thisscore += main.letterScores[b.containedLetter];
418
 
                            b.state = "dead";
419
 
                        })
420
 
                        main.selectedItems = [];
421
 
                        main.score += thisscore * wordlength;
422
 
                        scoredisplay.text = "" + (thisscore * wordlength);
423
 
                        showscoredisplay.start();
424
 
                    } else {
425
 
                        failure.play();
426
 
                        accum.text = "";
427
 
                        main.selectedItems.forEach(function(b) { b.selected = false; })
428
 
                        main.selectedItems = [];
429
 
                    }
430
 
                }
431
 
            }
432
 
        }
433
 
 
434
 
        Row {
435
 
            id: playarea
436
 
            anchors.top: main.top
437
 
            anchors.topMargin: 50
438
 
            anchors.left: main.left
439
 
            anchors.leftMargin: 2
440
 
            spacing: 2
441
 
            Repeater {
442
 
                model: lm
443
 
                Column {
444
 
                    add: Transition {
445
 
                        NumberAnimation { properties: "y"; easing.type: Easing.OutBounce; duration: 1000 }
446
 
                    }
447
 
                    move: Transition {
448
 
                        NumberAnimation { properties: "y"; easing.type: Easing.OutBounce }
449
 
                    }
450
 
                    property int idx: index
451
 
                    width: 48
452
 
                    height: main.height - 52
453
 
                    scale: -1
454
 
                    spacing: 2
455
 
                    Repeater {
456
 
                        model: letters
457
 
                        Rectangle {
458
 
                            property bool selected: false
459
 
                            property int idx: index
460
 
                            property string containedLetter: letter
461
 
                            id: box
462
 
                            clip: false
463
 
                            scale: -1
464
 
                            width: 48
465
 
                            height: 48
466
 
                            color: {
467
 
                                if (lm.get(parent.idx).letters.count >= 10) {
468
 
                                    return "red";
469
 
                                } else if (!selected) {
470
 
                                    return "#939598";
471
 
                                } else if (accum.isValid) {
472
 
                                    return "#93cc98";
473
 
                                } else {
474
 
                                    return "#cc9598"
475
 
                                }
476
 
                            }
477
 
                            radius: 3
478
 
                            y: main.height + box.height
479
 
                            Text {
480
 
                                anchors.centerIn: parent
481
 
                                text: letter
482
 
                                font.pixelSize: box.height * 0.6
483
 
                                color: "#ffffff"
484
 
                                font.family: "Helvetica"
485
 
                            }
486
 
                            MouseArea {
487
 
                                anchors.fill: parent
488
 
                                onClicked: {
489
 
                                    if (!box.selected) {
490
 
                                        box.selected = true;
491
 
                                        accum.text += letter;
492
 
                                        click.play();
493
 
                                        main.selectedItems[main.selectedItems.length] = box;
494
 
                                    } else {
495
 
                                        if (box === main.selectedItems[main.selectedItems.length - 1]) {
496
 
                                            main.selectedItems.pop(main.selectedItems.length - 1);
497
 
                                            box.selected = false;
498
 
                                            accum.text = accum.text.substr(0, accum.text.length - 1);
499
 
                                        }
500
 
                                    }
501
 
                                }
502
 
                            }
503
 
                            Behavior on opacity {
504
 
                                SequentialAnimation {
505
 
                                    ScriptAction { script: pulseEmitter.burst(1000); }
506
 
                                    NumberAnimation { properties:"opacity"; duration: 500 }
507
 
                                    ScriptAction { script: lm.get(box.parent.idx).letters.remove(box.idx); }
508
 
                                }
509
 
                            }
510
 
                            states: [
511
 
                                State { name: "alive" },
512
 
                                State {
513
 
                                    name: "dead"
514
 
                                    PropertyChanges { target: box; opacity: 0 }
515
 
                                }
516
 
                            ]
517
 
                            ParticleSystem {
518
 
                                id: particles
519
 
                                width: 200
520
 
                                height: 200
521
 
                                anchors.centerIn: parent
522
 
                                clip: false
523
 
                                ImageParticle {
524
 
                                    source: "redStar.png"
525
 
                                    alpha: 0
526
 
                                    colorVariation: 0.6
527
 
                                }
528
 
                                Emitter {
529
 
                                    id: pulseEmitter
530
 
                                    x: parent.width/2
531
 
                                    y: parent.height/2
532
 
                                    emitRate: 2000
533
 
                                    lifeSpan: 500
534
 
                                    enabled: false
535
 
                                    velocity: AngleDirection { magnitude: 256; angleVariation: 360; magnitudeVariation: 200; }
536
 
                                    size: 32
537
 
                                    sizeVariation: 8
538
 
                                }
539
 
                            }
540
 
                        }
541
 
                    }
542
 
                }
543
 
            }
544
 
        }
545
 
    }
546
 
}
 
 
b'\\ No newline at end of file'