commit
05e4d2e405
@ -0,0 +1,13 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += qml quick widgets
|
||||
|
||||
SOURCES += main.cpp
|
||||
|
||||
RESOURCES += qml.qrc
|
||||
|
||||
# Additional import path used to resolve QML modules in Qt Creator's code model
|
||||
QML_IMPORT_PATH =
|
||||
|
||||
# Default rules for deployment.
|
||||
include(deployment.pri)
|
@ -0,0 +1,23 @@
|
||||
android-no-sdk {
|
||||
target.path = /data/user/qt
|
||||
export(target.path)
|
||||
INSTALLS += target
|
||||
} else:android {
|
||||
x86 {
|
||||
target.path = /libs/x86
|
||||
} else: armeabi-v7a {
|
||||
target.path = /libs/armeabi-v7a
|
||||
} else {
|
||||
target.path = /libs/armeabi
|
||||
}
|
||||
export(target.path)
|
||||
INSTALLS += target
|
||||
} else:unix {
|
||||
isEmpty(target.path) {
|
||||
target.path = /opt/$${TARGET}/bin
|
||||
export(target.path)
|
||||
}
|
||||
INSTALLS += target
|
||||
}
|
||||
|
||||
export(INSTALLS)
|
@ -0,0 +1,12 @@
|
||||
#include <QApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.load(QUrl(QStringLiteral("qrc:///qml/main.qml")));
|
||||
|
||||
return app.exec();
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>qml/main.qml</file>
|
||||
<file>qml/2048.js</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -0,0 +1,298 @@
|
||||
var score = 0;
|
||||
var bestScore = 0;
|
||||
var gridSize = 4;
|
||||
var cellValues;
|
||||
var availableCells;
|
||||
//var labels = "PRC";
|
||||
var labels = "2048";
|
||||
var labelFunc;
|
||||
var targetLevel = 11;
|
||||
var checkTargetFlag = true;
|
||||
|
||||
switch (labels) {
|
||||
case "2048":
|
||||
labelFunc = function(n) {
|
||||
return Math.pow(2, n).toString();
|
||||
};
|
||||
break;
|
||||
case "PRC":
|
||||
labelFunc = function(n) {
|
||||
var dynasties = ["商", "周", "秦", "汉", "唐", "宋", "元", "明", "清", "ROC", "PRC"];
|
||||
return dynasties[n-1];
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
function startupFunction() {
|
||||
// Initialize variables
|
||||
score = 0;
|
||||
checkTargetFlag = true;
|
||||
var i;
|
||||
var j;
|
||||
|
||||
cellValues = new Array(gridSize);
|
||||
for (i = 0; i < gridSize; i++) {
|
||||
cellValues[i] = new Array(gridSize);
|
||||
for (j = 0; j < gridSize; j++)
|
||||
cellValues[i][j] = 0;
|
||||
}
|
||||
|
||||
updateAvailableCells();
|
||||
refreshCellViews(2);
|
||||
updateScore();
|
||||
console.log("Started a new game");
|
||||
}
|
||||
|
||||
function moveKey(event) {
|
||||
if ((event.key == Qt.Key_Q) && (event.modifiers & Qt.ControlModifier)) {
|
||||
Qt.quit();
|
||||
}
|
||||
|
||||
var isMoved = false;
|
||||
var i, j, v, v2;
|
||||
var oldScore = score;
|
||||
switch (event.key) {
|
||||
case Qt.Key_Left:
|
||||
for (i = 0; i < gridSize; i++) {
|
||||
v = cellValues[i];
|
||||
v2 = mergeVector(v);
|
||||
if (! arraysIdentical(v,v2)) {
|
||||
isMoved = true;
|
||||
cellValues[i] = v2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Right:
|
||||
for (i = 0; i < gridSize; i++) {
|
||||
v = cellValues[i].slice();
|
||||
v.reverse();
|
||||
v2 = mergeVector(v);
|
||||
if (! arraysIdentical(v,v2)) {
|
||||
isMoved = true;
|
||||
v2.reverse();
|
||||
cellValues[i] = v2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Up:
|
||||
for (i = 0; i < gridSize; i++) {
|
||||
v = cellValues.map(function(row) {return row[i];});
|
||||
v2 = mergeVector(v);
|
||||
if (! arraysIdentical(v,v2)) {
|
||||
isMoved = true;
|
||||
for (j = 0; j < gridSize; j++) {
|
||||
cellValues[j][i] = v2[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Down:
|
||||
for (i = 0; i < gridSize; i++) {
|
||||
v = cellValues.map(function(row) {return row[i];});
|
||||
v.reverse();
|
||||
v2 = mergeVector(v);
|
||||
if (! arraysIdentical(v,v2)) {
|
||||
isMoved = true;
|
||||
v2.reverse();
|
||||
for (j = 0; j < gridSize; j++) {
|
||||
cellValues[j][i] = v2[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (isMoved) {
|
||||
updateAvailableCells();
|
||||
refreshCellViews(1);
|
||||
if (oldScore !== score) {
|
||||
if (bestScore < score) {
|
||||
bestScore = score;
|
||||
}
|
||||
updateScore();
|
||||
if (checkTargetFlag && maxTileValue() >= targetLevel) {
|
||||
winMessage.open();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isDead()) {
|
||||
deadMessage.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ind2sub(ind) {
|
||||
var sub = [0, 0];
|
||||
sub[0] = Math.floor(ind / gridSize);
|
||||
sub[1] = ind % gridSize;
|
||||
return sub;
|
||||
}
|
||||
|
||||
function mergeVector(v0) {
|
||||
// Pass 1: remove zero elements
|
||||
var v = v0.slice();
|
||||
var i = v.length;
|
||||
while (i--) {
|
||||
if (v[i] === 0) {
|
||||
v.splice(i, 1);
|
||||
}
|
||||
}
|
||||
// Pass 2: merge same elements
|
||||
var v2 = [];
|
||||
while (v.length > 0) {
|
||||
if (v.length > 1 && v[0] === v[1]) {
|
||||
v2.push(v[0] + 1);
|
||||
score += parseInt(Math.pow(2, v[0]+1));
|
||||
v.splice(0, 2);
|
||||
} else {
|
||||
v2.push(v[0]);
|
||||
v.splice(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the gaps with zeros
|
||||
for (i = v2.length; i < v0.length; i++)
|
||||
v2[i] = 0;
|
||||
|
||||
return v2;
|
||||
}
|
||||
|
||||
function removeElementsWithValue(arr, val) {
|
||||
var i = arr.length;
|
||||
while (i--) {
|
||||
if (arr[i] === val) {
|
||||
arr.splice(i, 1);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function arraysIdentical(a, b) {
|
||||
var i = a.length;
|
||||
if (i !== b.length) return false;
|
||||
while (i--) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
function updateAvailableCells() {
|
||||
availableCells = [];
|
||||
for (var i = 0; i < gridSize; i++) {
|
||||
for (var j = 0; j < gridSize; j++) {
|
||||
if (cellValues[i][j] === 0) {
|
||||
availableCells.push(i * gridSize + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function refreshCellViews(n) {
|
||||
var i, sub;
|
||||
|
||||
// Popup a new number
|
||||
for (i = 0; i < n; i++) {
|
||||
var oneOrTwo = Math.random() < 0.9 ? 1: 2;
|
||||
var randomCellId = availableCells[Math.floor(Math.random() * availableCells.length)];
|
||||
|
||||
sub = ind2sub(randomCellId);
|
||||
cellValues[sub[0]][sub[1]] = oneOrTwo;
|
||||
|
||||
// Mark this cell as unavailable
|
||||
var idx = availableCells.indexOf(randomCellId);
|
||||
availableCells.splice(idx, 1);
|
||||
}
|
||||
|
||||
// Refresh the cell views
|
||||
for (i = 0; i < cells.count; i++) {
|
||||
sub = ind2sub(i);
|
||||
var cv = cellValues[sub[0]][sub[1]];
|
||||
var sty = computeTileStyle(cv);
|
||||
if ( cv === 0) {
|
||||
cells.itemAt(i).tileText = "";
|
||||
} else {
|
||||
cells.itemAt(i).tileText = labelFunc(cv);
|
||||
}
|
||||
cells.itemAt(i).color = sty.bgColor;
|
||||
cells.itemAt(i).tileColor = sty.fgColor;
|
||||
cells.itemAt(i).tileFontSize = sty.fontSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function updateScore() {
|
||||
scoreBoard.itemAt(0).scoreText = MyScript.score.toString();
|
||||
scoreBoard.itemAt(1).scoreText = MyScript.bestScore.toString();
|
||||
}
|
||||
|
||||
function isDead() {
|
||||
var dead = true;
|
||||
for (var i = 0; i < gridSize; i++) {
|
||||
for (var j = 0; j < gridSize; j++) {
|
||||
if (cellValues[i][j] === 0) {
|
||||
dead = false;
|
||||
}
|
||||
if (i > 0) {
|
||||
if (cellValues[i-1][j] === cellValues[i][j]) {
|
||||
dead = false;
|
||||
}
|
||||
}
|
||||
if (j > 0) {
|
||||
if (cellValues[i][j-1] === cellValues[i][j]) {
|
||||
dead = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dead;
|
||||
}
|
||||
|
||||
function computeTileStyle(n) {
|
||||
var fgColors = ["#776E62", "#F9F6F2"];
|
||||
var bgColors = ["#EEE4DA", "#EDE0C8", "#F2B179", "#F59563", "#F67C5F", "#F65E3B", "#EDCF72", "#EDCC61", "#EDC850", "#EDC53F", "#EDC22E", "#3C3A32"];
|
||||
var sty = {bgColor: helper.myColors.bggray,
|
||||
fgColor: fgColors[0],
|
||||
fontSize: 55 };
|
||||
if (n > 0) {
|
||||
if (n > 2)
|
||||
sty.fgColor = fgColors[1];
|
||||
if (n <= bgColors.length)
|
||||
sty.bgColor = bgColors[n-1];
|
||||
else
|
||||
sty.bgColor = bgColors[bgColors.length-1];
|
||||
}
|
||||
|
||||
if (labels === "2048") {
|
||||
/* Adjust font size according to size of the number
|
||||
[2, 100): 55
|
||||
[100, 1000): 45
|
||||
[1000, 2048]: 35
|
||||
> 2048: 30
|
||||
*/
|
||||
var pv = Math.pow(2, n);
|
||||
if (pv >= 100 && pv < 1000)
|
||||
sty.fontSize = 45;
|
||||
else if (pv >= 1000 && pv <= 2048)
|
||||
sty.fontSize = 35;
|
||||
else if (pv > 2048)
|
||||
sty.fontSize = 30;
|
||||
|
||||
}
|
||||
|
||||
return sty;
|
||||
}
|
||||
|
||||
function maxTileValue() {
|
||||
var mv = 0;
|
||||
for (var i = 0; i < gridSize; i++) {
|
||||
for (var j = 0; j < gridSize; j++) {
|
||||
var cv = cellValues[i][j];
|
||||
if ( mv < cv) {
|
||||
mv = cv;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mv;
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Dialogs 1.1
|
||||
import "2048.js" as MyScript
|
||||
|
||||
ApplicationWindow {
|
||||
visible: true
|
||||
width: 560
|
||||
height: 730
|
||||
title: qsTr("2048 Game");
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
Item {
|
||||
id: helper
|
||||
property var myColors: {"bglight": "#FAF8EF",
|
||||
"bggray": Qt.rgba(238/255, 228/255, 218/255, 0.35),
|
||||
"bgdark": "#BBADA0",
|
||||
"fglight": "#EEE4DA",
|
||||
"fgdark": "#776E62",
|
||||
"bgbutton": "#8F7A66", // Background color for the "New Game" button
|
||||
"fgbutton": "#F9F6F2" // Foreground color for the "New Game" button
|
||||
}
|
||||
}
|
||||
|
||||
color: helper.myColors.bglight
|
||||
focus: true
|
||||
Keys.onPressed: MyScript.moveKey(event)
|
||||
|
||||
Text {
|
||||
id: gameName
|
||||
x: 30
|
||||
y: 30
|
||||
font.pixelSize: 55
|
||||
font.bold: true
|
||||
text: "2048"
|
||||
color: helper.myColors.fgdark
|
||||
}
|
||||
|
||||
Row {
|
||||
y: 30
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
spacing: 5
|
||||
Repeater {
|
||||
id: scoreBoard
|
||||
model: 2
|
||||
Rectangle {
|
||||
width: (index == 0) ? 95 : 125
|
||||
height: 55
|
||||
radius: 3
|
||||
color: helper.myColors.bgdark
|
||||
property string scoreText: (index === 0) ? MyScript.score.toString() : MyScript.bestScore.toString()
|
||||
Text {
|
||||
text: (index == 0) ? "SCORE" : "BEST"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 7
|
||||
font.pixelSize: 13
|
||||
color: helper.myColors.fglight
|
||||
}
|
||||
Text {
|
||||
text: scoreText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 25
|
||||
font.pixelSize: 25
|
||||
font.bold: true
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: banner
|
||||
x: 30
|
||||
y: 120
|
||||
height: 40
|
||||
text: "Join the numbers and get to the <b>2048 tile!</b>"
|
||||
color: helper.myColors.fgdark
|
||||
font.pixelSize: 16
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Button {
|
||||
width: 129
|
||||
height: 40
|
||||
y: 120
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
style: ButtonStyle {
|
||||
background: Rectangle {
|
||||
color: helper.myColors.bgbutton
|
||||
radius: 3
|
||||
Text{
|
||||
anchors.centerIn: parent
|
||||
text: "New Game"
|
||||
color: helper.myColors.fgbutton
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
onClicked: MyScript.startupFunction()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
x: 30
|
||||
y: 200
|
||||
width: 500
|
||||
height: 500
|
||||
color: helper.myColors.bgdark
|
||||
radius: 6
|
||||
|
||||
Grid {
|
||||
x: 15;
|
||||
y: 15;
|
||||
rows: 4; columns: 4; spacing: 15
|
||||
|
||||
Repeater {
|
||||
id: cells
|
||||
model: 16
|
||||
Rectangle {
|
||||
width: 425/4; height: 425/4
|
||||
radius: 3
|
||||
color: helper.myColors.bggray
|
||||
property string tileText: ""
|
||||
property int tileFontSize: 55
|
||||
property color tileColor: helper.myColors.fgdark
|
||||
|
||||
Text {
|
||||
text: tileText
|
||||
color: tileColor
|
||||
font.pixelSize: tileFontSize
|
||||
font.bold: true
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MessageDialog {
|
||||
id: deadMessage
|
||||
title: "Game Over"
|
||||
text: "Game Over"
|
||||
standardButtons: StandardButton.Retry | StandardButton.Abort
|
||||
onAccepted: {
|
||||
MyScript.startupFunction();
|
||||
}
|
||||
onRejected: {
|
||||
Qt.quit();
|
||||
}
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
id: winMessage
|
||||
title: "You Win"
|
||||
text: "You win! Continue playing?"
|
||||
standardButtons: StandardButton.Yes | StandardButton.No
|
||||
onYes: {
|
||||
MyScript.checkTargetFlag = false;
|
||||
close()
|
||||
}
|
||||
onNo: MyScript.startupFunction()
|
||||
onRejected: {
|
||||
MyScript.checkTargetFlag = false;
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: MyScript.startupFunction()
|
||||
}
|
||||
}
|
Loading…
Reference in new issue