Commit e49779a1 authored by Gaël Porté's avatar Gaël Porté

Improve design for android app

parent 624c5036
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
......@@ -9,7 +9,6 @@ CONFIG(debug, debug|release) {
OBJECTS_DIR = $${DESTDIR}/obj
SOURC
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Refer to the documentation for the
......@@ -33,6 +32,10 @@ QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
android {
QMAKE_LINK+= -nostdlib++
}
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
......@@ -40,3 +43,58 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
HEADERS += \
src/creferee.h
DISTFILES += \
android/AndroidManifest.xml \
android/AndroidManifest.xml \
android/AndroidManifest.xml \
android/build.gradle \
android/build.gradle \
android/build.gradle \
android/gradle/wrapper/gradle-wrapper.jar \
android/gradle/wrapper/gradle-wrapper.jar \
android/gradle/wrapper/gradle-wrapper.jar \
android/gradle/wrapper/gradle-wrapper.properties \
android/gradle/wrapper/gradle-wrapper.properties \
android/gradle/wrapper/gradle-wrapper.properties \
android/gradlew \
android/gradlew \
android/gradlew \
android/gradlew.bat \
android/gradlew.bat \
android/gradlew.bat \
android/res/values/libs.xml \
android/res/values/libs.xml \
android/res/values/libs.xml
defineReplace(droidVersionCode) {
segments = $$split(1, ".")
for (segment, segments): vCode = "$$first(vCode)$$format_number($$segment, width=3 zeropad)"
contains(ANDROID_TARGET_ARCH, arm64-v8a): \
suffix = 01
else:contains(ANDROID_TARGET_ARCH, armeabi-v7a): \
suffix = 00
# add more cases as needed
return($$first(vCode)$$first(suffix))
}
VERSION = 1.0.0
ANDROID_VERSION_NAME = $$VERSION
ANDROID_VERSION_CODE = $$droidVersionCode($$ANDROID_VERSION_NAME)
contains(ANDROID_TARGET_ARCH,armeabi-v7a) {
ANDROID_PACKAGE_SOURCE_DIR = \
$$PWD/android
}
android {
QMAKE_LINK+= -nostdlib++
}
contains(ANDROID_TARGET_ARCH,arm64-v8a) {
ANDROID_PACKAGE_SOURCE_DIR = \
$$PWD/android
}
......@@ -9,6 +9,7 @@
"toolchain-version": "4.9",
"ndk-host": "linux-x86_64",
"target-architecture": "armeabi-v7a",
"android-package-source-directory": "/media/gael/DATA/Development/qtictactoe/android",
"android-version-name": "1.0.0",
"android-version-code": "00100000000",
"qml-root-path": "/media/gael/DATA/Development/qtictactoe",
......
<?xml version="1.0"?>
<manifest package="org.portegael.qtictactor" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="Initial Version" android:versionCode="1.0.0" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="25"/>
<manifest package="org.portegael.qtictactoe" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="Initial version" android:versionCode="1.0.0" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
......@@ -13,7 +13,7 @@
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="QTicTacToe" android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="portrait" android:launchMode="singleTop">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="QTicTacToe" android:screenOrientation="portrait" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
......@@ -23,7 +23,7 @@
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
<!-- Application arguments -->
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<meta-data android:name="android.app.lib_name" android:value="QTicTacToe"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
......
android/res/drawable-ldpi/icon.png

23.4 KB | W: | H:

android/res/drawable-ldpi/icon.png

98.3 KB | W: | H:

android/res/drawable-ldpi/icon.png
android/res/drawable-ldpi/icon.png
android/res/drawable-ldpi/icon.png
android/res/drawable-ldpi/icon.png
  • 2-up
  • Swipe
  • Onion skin
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
......@@ -9,19 +9,26 @@ FocusScope {
property string playerOneIconSource : "qrc:/Images/cross_02.png"
property string playerTwoIconSource : "qrc:/Images/circle_02.png"
// MouseArea to catch mouse events and hide panel
MouseArea {
Rectangle {
anchors.fill: parent
color: "black"
opacity: 0.8
onClicked: {
base.visible = false
// MouseArea to catch mouse events and hide panel
MouseArea {
anchors.fill: parent
onClicked: {
base.visible = false
}
}
}
Rectangle {
width: Style.boardGameWidthHeight
height: width
width: parent.width * 0.75 // Style.boardGameWidthHeight
height: parent.height * 0.5 // width
anchors.right : parent.right
anchors.bottom : parent.bottom
anchors.bottomMargin: 10
......@@ -52,35 +59,47 @@ FocusScope {
Text {
id: title
width: parent.width / 2
width: parent.width
anchors.top: parent.top
anchors.topMargin: 10
anchors.topMargin: 5
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Style.textFontSizeTitle
color: Style.titleColor
text: "Choose your symbol"
horizontalAlignment: Text.AlignHCenter
}
Row {
id: rowId
anchors.top: title.bottom
anchors.bottom: parent.bottom
anchors.left: title.left
width: title.width
height: parent.height - title.height
anchors.topMargin: 20
spacing: 20
width: parent.width
// height: parent.height - title.height - 50 // to be fixed
anchors.topMargin: 5
anchors.bottomMargin: 5
anchors.leftMargin: 10
anchors.rightMargin: anchors.leftMargin
anchors.horizontalCenter: parent.horizontalCenter
// Cross symbol panel
ListView {
width: 100
height: parent.height - 50
id: listViewId
width: parent.width * 0.4
height: parent.height
orientation: Qt.Vertical
model: listModelCrossId
delegate:
Rectangle { width : 100; height: width;
Rectangle { width : height; height: listViewId.height * 0.3;
border.color: Style.backgroundColor;
border.width: mouseAreaCrossId.containsMouse ? 2 : 1;
anchors.horizontalCenter: parent.horizontalCenter
color : {
if(base.playerOneIconSource === imageSource || mouseAreaCrossId.containsMouse ) {
Style.filledSquareColor
......@@ -107,8 +126,8 @@ FocusScope {
// Separator
Rectangle {
width: 50
height: parent.height - 80
width: parent.width * 0.2
height: parent.height * 0.95
color: Style.emptySquareColor
Rectangle{
......@@ -122,14 +141,15 @@ FocusScope {
// Circle symbol panel
ListView {
width: 100
height: parent.height - 50
width: parent.width * 0.4
height: parent.height
orientation: Qt.Vertical
model: listModelCircleId
delegate:
Rectangle { width : 100; height: width;
Rectangle { width : height; height: listViewId.height * 0.3;
border.color: Style.backgroundColor;
border.width: mouseAreaCircleId.containsMouse ? 2 : 1;
anchors.horizontalCenter: parent.horizontalCenter
color : if(base.playerTwoIconSource === imageSource || mouseAreaCircleId.containsMouse ) {
Style.filledSquareColor
}
......@@ -147,24 +167,7 @@ FocusScope {
onClicked: {
base.playerTwoIconSource = imageSource;
}
}}
}
}
/// Hide icon
Image {
source: "qrc:/Images/hide_icon_selection.png"
height: 40
fillMode: Image.PreserveAspectFit
anchors.right : parent.right
anchors.bottom: parent.bottom
anchors.margins: 20
MouseArea {
anchors.fill: parent
onClicked: {
base.visible = false
}
}
}
}
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
src/Images/circle.png

15.9 KB | W: | H:

src/Images/circle.png

2.61 KB | W: | H:

src/Images/circle.png
src/Images/circle.png
src/Images/circle.png
src/Images/circle.png
  • 2-up
  • Swipe
  • Onion skin
File mode changed from 100644 to 100755
src/Images/cross.png

9.09 KB | W: | H:

src/Images/cross.png

1.36 KB | W: | H:

src/Images/cross.png
src/Images/cross.png
src/Images/cross.png
src/Images/cross.png
  • 2-up
  • Swipe
  • Onion skin
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
......@@ -4,11 +4,11 @@ import QtQuick 2.0
QtObject {
readonly property int boardGameWidthHeight : 500
readonly property int headerFooterHeight : 60
readonly property int boardGameWidthHeight : widthScreen * 0.85
readonly property int headerFooterHeight : heightScreen * 0.1
readonly property int textFontSize : 16
readonly property int textFontSizeTitle : 30
readonly property int textFontSizeTitle : 25
readonly property string backgroundColor : "#222222"
readonly property string emptySquareColor : "#333333"
......
#include "creferee.h"
#include <qdebug.h>
#include <unistd.h>
#include <QtGlobal>
#include <QCoreApplication>
#include <QTimer>
CReferee::CReferee(QObject *parent) : QObject(parent),
m_winner(PlayerEnum::Player::Undefined),
......@@ -8,10 +11,14 @@ CReferee::CReferee(QObject *parent) : QObject(parent),
m_scorePlayerOne(0),
m_scorePlayerTwo(0),
m_isDrawGame(false),
m_isAiActivated(false)
m_isAiActivated(false),
m_delayTimerValue(250)
{
QVector<int> m_qmlBoadGameArray(9);
m_timer = new QTimer();
connect(m_timer, &QTimer::timeout, this, &CReferee::timeoutSlot);
resetGame();
emit playerWon();
......@@ -32,9 +39,9 @@ void CReferee::playerPlayed(const int index)
m_qmlBoadGameArray[index] = (m_currentPlayer == PlayerEnum::Player::One ? PlayerEnum::TokenType::Cross : PlayerEnum::TokenType::Circle);
// qDebug() << "| " << m_qmlBoadGameArray.at(0) << " | " << m_qmlBoadGameArray.at(1) << " | " << m_qmlBoadGameArray.at(2) << " | ";
// qDebug() << "| " << m_qmlBoadGameArray.at(3) << " | " << m_qmlBoadGameArray.at(4) << " | " << m_qmlBoadGameArray.at(5) << " | ";
// qDebug() << "| " << m_qmlBoadGameArray.at(6) << " | " << m_qmlBoadGameArray.at(7) << " | " << m_qmlBoadGameArray.at(8) << " | ";
// qDebug() << "| " << m_qmlBoadGameArray.at(0) << " | " << m_qmlBoadGameArray.at(1) << " | " << m_qmlBoadGameArray.at(2) << " | ";
// qDebug() << "| " << m_qmlBoadGameArray.at(3) << " | " << m_qmlBoadGameArray.at(4) << " | " << m_qmlBoadGameArray.at(5) << " | ";
// qDebug() << "| " << m_qmlBoadGameArray.at(6) << " | " << m_qmlBoadGameArray.at(7) << " | " << m_qmlBoadGameArray.at(8) << " | ";
emit qmlBoardGameArrayChanged();
......@@ -53,11 +60,14 @@ void CReferee::playerPlayed(const int index)
return;
}
// If AI is activated, it plays as Player Two
if(m_isAiActivated && m_currentPlayer == PlayerEnum::Player::One) {
m_currentPlayer = PlayerEnum::Player::Two;
makeIaMove();
// Set timer to display
m_timer->start(static_cast<int>(m_delayTimerValue));
}
else {
// Switch player
......@@ -85,6 +95,13 @@ void CReferee::resetGame(void)
emit qmlBoardGameArrayChanged();
}
void CReferee::timeoutSlot()
{
m_timer->stop();
makeIaMove();
}
bool CReferee::checkIfPlayerWon(const int index)
{
int tokenType = m_currentPlayer == PlayerEnum::Player::One ? PlayerEnum::TokenType::Circle : PlayerEnum::TokenType::Cross;
......@@ -167,14 +184,248 @@ void CReferee::insertToMultidimensionalArray(const int index, const int tokenTyp
}
}
int CReferee::getIndexSingleArrary(const int row, const int col) {
if(row == 0 && col == 0) {
return 0 ;
}
else if(row == 1 && col == 0) {
return 1 ;
}
else if(row == 2 && col == 0) {
return 2 ;
}
else if(row == 0 && col == 1) {
return 3 ;
}
else if(row == 1 && col == 1) {
return 4 ;
}
else if(row == 2 && col == 1) {
return 5 ;
}
else if(row == 0 && col == 2) {
return 6 ;
}
else if(row == 1 && col == 2) {
return 7 ;
}
else if(row == 2 && col == 2) {
return 8 ;
}
return -1;
}
void CReferee::insertToSingleArrary(const int row, const int col, const int tokenType) {
m_qmlBoadGameArray[getIndexSingleArrary(row, col)] = tokenType;
}
void CReferee::makeIaMove()
{
for(int i=0; i < 9; i++) {
if(m_qmlBoadGameArray.at(i) != PlayerEnum::TokenType::Cross && m_qmlBoadGameArray.at(i) != PlayerEnum::TokenType::Circle) {
playerPlayed(i);
break;
Move bestMove = findBestMove(m_boardGameArray);
qDebug() <<"The Optimal Move is ROW: " << bestMove.row << " COL: " << bestMove.col ;
int indexToPlay = getIndexSingleArrary(bestMove.row, bestMove.col);
qDebug() <<"Index : " << indexToPlay << "\n";
playerPlayed(indexToPlay);
emit qmlBoardGameArrayChanged();
}
// This function returns true if there are moves
// remaining on the board. It returns false if
// there are no moves left to play.
bool CReferee::isMovesLeft(int board[3][3]) {
for (int i = 0; i<3; i++)
for (int j = 0; j<3; j++)
if (board[i][j] != PlayerEnum::TokenType::Cross && board[i][j] != PlayerEnum::TokenType::Circle )
return true;
return false;
}
// This is the evaluation function as discussed
// in the previous article ( http://goo.gl/sJgv68 )
int CReferee::evaluate(int b[3][3])
{
// Checking for Rows for X or O victory.
for (int row = 0; row<3; row++)
{
if (b[row][0] == b[row][1] &&
b[row][1] == b[row][2])
{
if (b[row][0] == PlayerEnum::TokenType::Cross)
return +10;
else if (b[row][0] == PlayerEnum::TokenType::Circle)
return -10;
}
}
emit qmlBoardGameArrayChanged();
// Checking for Columns for X or O victory.
for (int col = 0; col<3; col++)
{
if (b[0][col] == b[1][col] &&
b[1][col] == b[2][col])
{
if (b[0][col] == PlayerEnum::TokenType::Cross)
return +10;
else if (b[0][col] == PlayerEnum::TokenType::Circle)
return -10;
}
}
// Checking for Diagonals for X or O victory.
if (b[0][0] == b[1][1] && b[1][1] == b[2][2])
{
if (b[0][0] == PlayerEnum::TokenType::Cross)
return +10;
else if (b[0][0] == PlayerEnum::TokenType::Circle)
return -10;
}
if (b[0][2]==b[1][1] && b[1][1]==b[2][0])
{
if (b[0][2] == PlayerEnum::TokenType::Cross)
return +10;
else if (b[0][2] == PlayerEnum::TokenType::Circle)
return -10;
}
// Else if none of them have won then return 0
return 0;
}
// This is the minimax function. It considers all
// the possible ways the game can go and returns
// the value of the board
int CReferee::minimax(int board[3][3], int depth, bool isMax)
{
int score = evaluate(board);
// If Maximizer has won the game return his/her
// evaluated score
if (score == 10)
return score;
// If Minimizer has won the game return his/her
// evaluated score
if (score == -10)
return score;
// If there are no more moves and no winner then
// it is a tie
if (isMovesLeft(board)==false)
return 0;
// If this maximizer's move
if (isMax)
{
int best = -1000;
// Traverse all cells
for (int i = 0; i<3; i++)
{
for (int j = 0; j<3; j++)
{
// Check if cell is empty
if ((board[i][j] != PlayerEnum::TokenType::Cross) && (board[i][j] != PlayerEnum::TokenType::Circle) ) {
// Make the move
board[i][j] = PlayerEnum::TokenType::Cross;
// Call minimax recursively and choose
// the maximum value
best = qMax( best,
minimax(board, depth+1, !isMax) );
// Undo the move
board[i][j] = PlayerEnum::TokenType::Reset;
}
}
}
return best;
}
// If this minimizer's move
else
{
int best = 1000;
// Traverse all cells
for (int i = 0; i<3; i++)
{
for (int j = 0; j<3; j++)
{
// Check if cell is empty
if (board[i][j] != PlayerEnum::TokenType::Cross && board[i][j] != PlayerEnum::TokenType::Circle ) {
// Make the move
board[i][j] = PlayerEnum::TokenType::Circle;
// Call minimax recursively and choose
// the minimum value
best = qMin(best,
minimax(board, depth+1, !isMax));
// Undo the move
board[i][j] = PlayerEnum::TokenType::Reset;
}
}
}
return best;
}
}
// This will return the best possible move for the player
Move CReferee::findBestMove(int board[3][3])
{
int bestVal = -1000;
Move bestMove;
bestMove.row = -1;
bestMove.col = -1;
// Traverse all cells, evaluate minimax function for
// all empty cells. And return the cell with optimal
// value.
for (int i = 0; i<3; i++)
{
for (int j = 0; j<3; j++)