diff --git a/FA/Entertainment/TicTacToeGame/README.md b/FA/Entertainment/TicTacToeGame/README.md new file mode 100644 index 0000000000000000000000000000000000000000..31944d9123bb7ac49aaccf13255dacb0f82ad074 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/README.md @@ -0,0 +1,71 @@ +# 井字过三关小游戏 + +### 简介 + +Demo基于Open Harmony系统使用ETS语言进行编写,本Demo主要通过设备认证、,分布式拉起,分布式数据管理等功能来实现。 + +1. ##### 设备认证 + + 陌生设备开始游戏前需要进行设备认证,使用[DeviceManager](https://gitee.com/openharmony/device_manager)组件,DeviceManager组件在OpenHarmony上提供账号无关的分布式设备的认证组网能力,并为开发者提供了一套用于分布式设备间监听、发现和认证的接口。 + +2. ##### 分布式拉起 + + 通过使用[FeatureAbility](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/apis/js-apis-featureAbility.md)模块的startAbility接口,远程拉起其他在线设备。 + +3. ##### 分布式数据管理 + + 使用[distributedData](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/apis/js-apis-distributed-data.md)模块同步设备之间的数据。 + + + +### 快速上手 + +#### 1. Hi3516环境准备 + +- 获取Open Harmony源代码,OpenHarmony版本须3.0LTS以上,参考[源码下载](https://www.openharmony.cn/pages/extra/70a9ad/) +- 搭建Linux基础环境,安装编译依赖基础软件,参考[安装开发板环境](https://www.openharmony.cn/pages/0001000400/#%E5%B0%86linux-shell%E6%94%B9%E4%B8%BAbash) +- 编译以及烧录,参考[开发板(编译、烧录)](https://www.openharmony.cn/pages/0001000401/#%E6%96%B0%E5%BB%BA%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F) + +#### 2. 应用编译环境准备 + +- 下载DevEco Studio 3.0.0.601及以上版本 [下载地址](https://developer.harmonyos.com/cn/develop/deveco-studio#download_beta) +- 配置SDK,参考 [配置OpenHarmony-SDK](https://www.openharmony.cn/pages/00090001/#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6) +- DevEco Studio 点击File -> Open 导入本工程 + +#### 3. 安装应用 + +- 对应用进行签名,可以参考[配置OpenHarmony应用签名信息](https://www.openharmony.cn/pages/00090003/#%E7%94%9F%E6%88%90%E5%AF%86%E9%92%A5%E5%92%8C%E8%AF%81%E4%B9%A6%E8%AF%B7%E6%B1%82%E6%96%87%E4%BB%B6) + +- 安装应用 + + 打开**OpenHarmony SDK路径 \toolchains** 文件夹下,执行如下hdc_std命令,其中**path**为hap包所在路径。 + + ``` + hdc_std file install path\entry-debug-standard-ark-signed.hap + ``` + + + +### 应用效果 + +1. ##### 设备认证,开始游戏前,如果是陌生设备需要先通过认证 + + ![deviceAuthentication](./resource/deviceAuthentication.gif) + +2. ##### 开始游戏,如果设备之前认证过了,可以直接开始游戏 + + ![startGame](./resource/startGame.gif) + +#### 如何体验 + +##### 1. 将所有设备连入同一局域网 + +##### 2. 应用内点击开始游戏进行设备认证,并拉起远程设备端应用 + + + + + +#### 相关链接 + +##### [从零开发井字过三关小游戏](./quick_develop.md) diff --git a/FA/Entertainment/TicTacToeGame/build.gradle b/FA/Entertainment/TicTacToeGame/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..f0245209fa88c0d2ea7c8e4d42a1eac664e530df --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/build.gradle @@ -0,0 +1,34 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +apply plugin: 'com.huawei.ohos.app' + +//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#section1112183053510 +ohos { + compileSdkVersion 7 + supportSystem "standard" +} + +buildscript { + repositories { + maven { + url 'https://repo.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + } + dependencies { + classpath 'com.huawei.ohos:hap:3.0.3.4' + classpath 'com.huawei.ohos:decctest:1.2.6.0' + } +} + +allprojects { + repositories { + maven { + url 'https://repo.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + } +} diff --git a/FA/Entertainment/TicTacToeGame/entry/build.gradle b/FA/Entertainment/TicTacToeGame/entry/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..26136bbe0cf099274e33998cee090228b50fe002 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/entry/build.gradle @@ -0,0 +1,21 @@ +apply plugin: 'com.huawei.ohos.hap' +//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#section1112183053510 +ohos { + compileSdkVersion 7 + defaultConfig { + compatibleSdkVersion 7 + } + buildTypes { + release { + proguardOpt { + proguardEnabled false + rulesFiles 'proguard-rules.pro' + } + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.har']) + testImplementation 'junit:junit:4.13.1' +} diff --git a/FA/Entertainment/TicTacToeGame/entry/proguard-rules.pro b/FA/Entertainment/TicTacToeGame/entry/proguard-rules.pro new file mode 100644 index 0000000000000000000000000000000000000000..f7666e47561d514b2a76d5a7dfbb43ede86da92a --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/entry/proguard-rules.pro @@ -0,0 +1 @@ +# config module specific ProGuard rules here. \ No newline at end of file diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/config.json b/FA/Entertainment/TicTacToeGame/entry/src/main/config.json new file mode 100644 index 0000000000000000000000000000000000000000..3a9e3f2f85723b79e02db1bff7e643d4b9b914cb --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/entry/src/main/config.json @@ -0,0 +1,67 @@ +{ + "app": { + "bundleName": "com.example.tictactoegame", + "vendor": "example", + "version": { + "code": 1000000, + "name": "1.0.0" + } + }, + "deviceConfig": {}, + "module": { + "package": "com.example.tictactoegame", + "name": ".MyApplication", + "mainAbility": ".MainAbility", + "deviceType": [ + "phone" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry", + "installationFree": false + }, + "abilities": [ + { + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + "orientation": "unspecified", + "visible": true, + "srcPath": "MainAbility", + "name": ".MainAbility", + "srcLanguage": "ets", + "icon": "$media:icon", + "description": "$string:description_mainability", + "formsEnabled": false, + "label": "$string:entry_MainAbility", + "type": "page", + "launchType": "standard" + } + ], + "js": [ + { + "mode": { + "syntax": "ets", + "type": "pageAbility" + }, + "pages": [ + "pages/GameMain", + "pages/Fight" + ], + "name": ".MainAbility", + "window": { + "designWidth": 720, + "autoDesignWidth": false + } + } + ] + } +} \ No newline at end of file diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/app.ets b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/app.ets new file mode 100644 index 0000000000000000000000000000000000000000..bf28e58b36cf11db491d3f00768ef2c9e41d05cf --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/app.ets @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default { + onCreate() { + console.info('Application onCreate') + }, + onDestroy() { + console.info('Application onDestroy') + }, +} \ No newline at end of file diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/Chess.ets b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/Chess.ets new file mode 100644 index 0000000000000000000000000000000000000000..1e536bd186f2e4d8e5dc96e4cca8f97a1a0657d9 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/Chess.ets @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import prompt from '@system.prompt'; + +const EMPTY = 0 +const FORK = 1 +const CIRCLE = 2 +const CONTINUE = 3 + +export default class Chess { + gridItemValue = [256, 128, 64, 32, 16, 8, 4, 2, 1] + used = [] + result_X = 0 + result_O = 0 + + constructor() { + } + + initChess() { + this.result_O = 0 + this.result_X = 0 + for (let i = 0; i < 9; i++) { + this.used[i] = false + } + } + + pushChess(indexId, cur) { + let id = Number.parseInt(indexId) + if (this.used[id]) return false + this.used[id] = true + if (cur === FORK) { + this.result_X |= this.gridItemValue[id] + } else { + this.result_O |= this.gridItemValue[id] + } + } + + allIsTrue(){ + for(let i = 0; i < 9; i++){ + this.used[i] = true + } + } + + isWin() { + let x = this.result_X + let o = this.result_O + if (448 == (x & 448) || 292 == (x & 292) || 273 == (x & 273) || 146 == (x & 146) || 73 == (x & 73) || 56 == (x & 56) || 7 == (x & 7) || 84 == (x & 84)){ + this.allIsTrue() + return FORK + } + if (448 == (o & 448) || 292 == (o & 292) || 273 == (o & 273) || 146 == (o & 146) || 73 == (o & 73) || 56 == (o & 56) || 7 == (o & 7) || 84 == (o & 84)) { + this.allIsTrue() + return CIRCLE + } + let res = 0; + for (let i = 0; i < 9; i++) { + if (this.used[i] == false) { + res = 1 + break + } + } + if (res === 1) { + return CONTINUE + } + return EMPTY + } +} \ No newline at end of file diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/Fight.ets b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/Fight.ets new file mode 100644 index 0000000000000000000000000000000000000000..6675c51b86571c9a9b02054cc64ba7df6208ff84 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/Fight.ets @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import RemoteDataManager from './RemoteDataManager.ets'; +import Chess from './Chess.ets' + +const EMPTY = 0 +const FORK = 1 +const CIRCLE = 2 +const CONTINUE = 3 +const YOU_WIN = 4 +const YOU_LOSE = 5 +const TIE = 6 + +let result + +@Entry +@Component +struct fightEnter { + private current_O = $r('app.media.user_circled_current') + private self_O = $r('app.media.user_circled_me') + private current_X = $r("app.media.user_fork_current") + private self_X = $r("app.media.user_fork_me") + private fork = $r('app.media.fork') + private circle = $r("app.media.circled") + private whiteBg = $r("app.media.littleBK") + private curChess + private selfChess = EMPTY + @State gridVis: any[] = [] + vis = [] + action = true + remoteDataManager = new RemoteDataManager() + chess = new Chess() + + initGame() { + for (let i = 0; i < 9; i++) { + this.gridVis[i] = { value: EMPTY, id: i } + this.vis[i] = false + } + this.curChess = FORK + this.selfChess = EMPTY + this.action = true + this.chess.initChess() + result = CONTINUE + } + + aboutToAppear() { + this.initGame() + } + + onPageShow() { + this.remoteDataManager.registerDataListCallback(() => { + + let arr = this.remoteDataManager.arr[0] + if (arr.value.value[7] === 'X') { + this.curChess = CIRCLE + if (this.selfChess === EMPTY) this.selfChess = this.curChess + this.gridVis[Number.parseInt(arr.key)] = { value: FORK, id: Number.parseInt(arr.key) } + this.chess.pushChess(arr.key, FORK) + this.action = true + } else if(arr.value.value[7] === 'O'){ + this.action = true + this.curChess = FORK + if (this.selfChess === EMPTY)this.selfChess = this.curChess + this.gridVis[Number.parseInt(arr.key)] = { value: CIRCLE, id: Number.parseInt(arr.key) } + this.chess.pushChess(arr.key, CIRCLE) + } + this.judgeWin() + }) + } + + dealPoint(item) { + if (false === this.action) return + if (CONTINUE !== result) return + this.action = false + if (this.selfChess === EMPTY)this.selfChess = this.curChess + + if (this.curChess === FORK) { + this.gridVis[item.id] = { value: this.curChess, id: item.id } + this.remoteDataManager.dataChange(item.id, "value_X_test_string") + this.chess.pushChess(item.id, FORK) + this.curChess = CIRCLE + } else { + this.gridVis[item.id] = { value: this.curChess, id: item.id } + this.remoteDataManager.dataChange(item.id, "value_O_test_string") + this.chess.pushChess(item.id, CIRCLE) + this.curChess = FORK + } + this.judgeWin() + } + + judgeWin() { + let res = this.chess.isWin() + switch (res) { + case FORK: + if (this.selfChess === FORK) { + result = YOU_WIN + } else { + result = YOU_LOSE + } + break + case CIRCLE: + if (this.selfChess === CIRCLE) { + result = YOU_WIN + } else { + result = YOU_LOSE + } + break + case EMPTY: + result = TIE + break + default: + } + } + + build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Row({ useAlign: HorizontalAlign.Start }) { + if (this.selfChess === EMPTY) { + Image(this.current_X).height(150).width('30%') + } else if (this.curChess === CIRCLE) { + Image(this.current_O).height(150).width('30%') + } else { + Image(this.current_X).height(150).width('30%') + } + Text("").width('40%') + if (this.selfChess === EMPTY) { + Image(this.whiteBg).height(150).width('30%') + } else { + Image(this.selfChess === FORK ? this.self_X : this.self_O).height(150).width('30%') + } + }.height('30%') + Flex() { + Stack() { + Grid() { + ForEach(this.gridVis, (item) => { + GridItem() { + if (item.value === FORK) { + Image(this.fork).backgroundColor(Color.White) + } else if (item.value === CIRCLE) { + Image(this.circle).backgroundColor(Color.White) + } else { + Image(this.whiteBg) + } + }.onClick(() => { + this.dealPoint(item) + }) + }, item => item.id) + }.columnsTemplate('1fr 1fr 1fr').rowsTemplate('1fr 1fr 1fr').columnsGap(3).rowsGap(3).align(Alignment.Center).alignSelf(ItemAlign.Center).backgroundColor('#9F5F9F') + + if (result === YOU_WIN) { + Image($r("app.media.game_win")).width(224).height(224) + } else if (result === YOU_LOSE) { + Image($r("app.media.game_fail")).width(224).height(224) + } else if (result === TIE) { + Image($r("app.media.game_tie")).width(224).height(224) + } + } + }.height('50%') + + + Row({ useAlign: HorizontalAlign.Center }) { + Button('重新开始').width(200).height(50).fontSize(30).backgroundColor('#8E6B23').onClick(() => { + this.initGame() + }) + }.height('20%') + } + } +} \ No newline at end of file diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/GameMain.ets b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/GameMain.ets new file mode 100644 index 0000000000000000000000000000000000000000..f96164378ab4d7604c90970b6c1cb1179edf0187 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/GameMain.ets @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import router from '@system.router'; +import RemoteDeviceModel from './RemoteDeviceModel'; +import featureAbility from '@ohos.ability.featureAbility'; +import prompt from '@system.prompt'; + +@CustomDialog +struct gameRules { + controller: CustomDialogController + + build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Text('游戏规则').fontColor(Color.Black).fontSize(30) + }.width('100%').height('30%') + + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { + Text('两个玩家,一个打圈(O),一个打叉(X),轮流在3X3的表格上打自己的符号,最先以横丶直丶斜连成一线则为胜。如果格子填满没有出现胜利者,将得和局。') + .textOverflow({ overflow: TextOverflow.None }) + .fontSize(25).width('80%') + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('确定').width(200).height(50).fontSize(30).onClick(() => { + this.controller.close() + }) + }.height('50%') + }.width('100%').height('70%') + } + .height('80%') + .width('100%') + } +} + +@CustomDialog +struct gameStart { + controller: CustomDialogController + REMOTE_ABILITY_STARTED = 'remoteAbilityStarted' + @State deviceName: any[] = [] + @State check: any[] = [] + remoteDeviceModel = new RemoteDeviceModel() + + aboutToAppear() { + this.remoteDeviceModel.registerDeviceListCallback(() => { + let count = 0 + this.deviceName[count] = { value: '', id: count, deviceName: "本机" } + this.check[count] = true + count++ + for (let i = 0; i < this.remoteDeviceModel.deviceList.length; i++) { + this.deviceName[count] = { + value: this.remoteDeviceModel.deviceList[i].deviceId, + id: count, + deviceName: this.remoteDeviceModel.deviceList[i].deviceName + } + this.check[count] = false + count++ + } + + for (let i = 0; i < this.remoteDeviceModel.discoverList.length; i++) { + this.deviceName[count] = { + value: this.remoteDeviceModel.discoverList[i].deviceId, + id: count, + deviceName: this.remoteDeviceModel.discoverList[i].deviceName + } + this.check[count] = false + count++ + } + + }) + } + + aboutToDisappear() { + this.remoteDeviceModel.unregisterDeviceListCallback() + } + + startAbilityContinuation(deviceId) { + let wantValue = { + bundleName: 'com.example.tictactoegame', + abilityName: 'com.example.tictactoegame.MainAbility', + deviceId: deviceId, + parameters: { + uri: 'pages/Fight' + } + }; + featureAbility.startAbility({ want: wantValue }).then(() => { + router.replace({ uri: 'pages/Fight' }) + }); + } + + build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Text('发现以下在线设备').fontColor(Color.Black).fontSize(30) + }.width('100%').height('20%') + + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { + List() { + ForEach(this.deviceName, (item) => { + ListItem() { + Row() { + Text(item.deviceName).width('80%').fontSize(30).fontColor(Color.Black) + Radio({ value: '' }).checked(this.check[item.id]).onChange(() => { + for (let i = 0; i < this.check.length; i++) { + this.check[i] = false + } + this.check[item.id] = true + }) + } + } + }, item => item.id) + } + .height('80%') + + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('确定').width(200).height(50).fontSize(30).onClick(() => { + for (let i = 0; i < this.check.length; i++) { + if (this.check[i] == true) { + for (let j = 0; j < this.remoteDeviceModel.discoverList.length; j++) { + if (this.remoteDeviceModel.discoverList[j].deviceId == this.deviceName[i].value) { + this.remoteDeviceModel.authDevice(this.remoteDeviceModel.discoverList[j], (err,data) => { + this.startAbilityContinuation(this.deviceName[i].value) + }) + } + } + + for (let j = 0; j < this.remoteDeviceModel.deviceList.length; j++) { + if (this.remoteDeviceModel.deviceList[j].deviceId == this.deviceName[i].value) { + this.startAbilityContinuation(this.deviceName[i].value) + } + } + } + } + this.controller.close() + }) + }.height('30%') + + } + }.height('100%').width('80%') + } +} + +@Entry +@Component +struct GameMain { + rulesDialog: CustomDialogController = new CustomDialogController({ + builder: gameRules(), + autoCancel: true + }) + startDialog: CustomDialogController = new CustomDialogController({ + builder: gameStart(), + autoCancel: true + }) + + aboutToAppear() { + featureAbility.getWant((err, want) => { + if (want.parameters.uri == 'pages/Fight') { + router.replace({ uri: 'pages/Fight' }) + } + }) + } + + build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Stack() { + Image($r("app.media.bg")) + Column() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Text('井字游戏大作战').fontSize(30).textAlign(TextAlign.Center).fontColor(Color.White) + }.height('10%') + + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { + Stack() { + Image($r("app.media.bg_start_game")) + Column() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Image($r("app.media.battles")).height(100).width(100) + }.height('50%') + + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('开始游戏').height(50).width(200).backgroundColor('#32CD99').fontSize(20).onClick(() => { + this.startDialog.open() + }) + Text("").height(20) + Text('游戏规则').fontSize(20).fontColor('#FF00FF').height(60).decoration({ type: TextDecorationType.Underline, color: Color.Red }).onClick(() => { + this.rulesDialog.open() + }) + }.height('40%') + } + } + }.height('50%').width('90%') + + Flex() { + }.height('30%') + } + } + }.width('100%').height('100%') + } +} \ No newline at end of file diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/RemoteDataManager.ets b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/RemoteDataManager.ets new file mode 100644 index 0000000000000000000000000000000000000000..73593548805f7932025890f6cb60ceb5d58d4be0 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/RemoteDataManager.ets @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import prompt from '@system.prompt'; +import factory from '@ohos.data.distributedData'; + +export default class RemoteDataManager { + kvManager = null + kvStore = null + STORE_ID = 'store_ttt' + arr + + constructor() { + } + + registerDataListCallback(callback) { + let that = this + if (this.kvManager == null) { + try { + const config = { + userInfo: { + userId: '0', + userType: 0 + }, + bundleName: 'com.example.tictactoegame' + } + factory.createKVManager(config).then((manager) => { + that.kvManager = manager + that.registerDataListCallback_(callback) + }).catch((err) => { + }) + } catch (e) { + } + } else { + this.registerDataListCallback_(callback) + } + } + + registerDataListCallback_(callback) { + let that = this + if (that.kvManager == null) { + callback() + return + } + if (that.kvStore == null) { + try { + let options = + { + createIfMissing: true, + encrypt: false, + backup: false, + autoSync: true, + kvStoreType: 1, + securityLevel: 3 + } + this.kvManager.getKVStore(this.STORE_ID, options).then((store) => { + that.kvStore = store + that._registerDataListCallback_(callback) + }).catch((err) => { + }) + } catch (e) { + } + } else { + this._registerDataListCallback_(callback) + } + } + + _registerDataListCallback_(callback) { + let that = this + if (that.kvManager == null) { + callback() + return + } + this.kvStore.on('dataChange', 1, function(data) { + if (data) { + that.arr = data.updateEntries + callback() + } + }) + } + + dataChange(key, value) { + let that = this + try { + that.kvStore.put(JSON.stringify(key), JSON.stringify(value)).then((data) => { + }).catch((err) => { + prompt.showToast({message:'put err:'+JSON.stringify(value)}) + }) + + } catch (e) { + } + } + +} \ No newline at end of file diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/RemoteDeviceModel.ets b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/RemoteDeviceModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..b6b165990ddf52e843f558efd514215e503ae586 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/entry/src/main/ets/MainAbility/pages/RemoteDeviceModel.ets @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import deviceManager from '@ohos.distributedHardware.deviceManager'; +import prompt from '@system.prompt'; + +var SUBSCRIBE_ID = 100; + +export default class RemoteDeviceModel { + deviceList = []; + discoverList = []; + callback; + authCallback = null; + #deviceManager; + + constructor() { + } + + registerDeviceListCallback(callback) { + if (typeof (this.#deviceManager) === 'undefined') { + deviceManager.createDeviceManager('com.example.tictactoegame', (error, value) => { + if (error) return + this.#deviceManager = value; + this.registerDeviceListCallback_(callback); + }); + } else { + this.registerDeviceListCallback_(callback); + } + } + + registerDeviceListCallback_(callback) { + let that = this + this.callback = callback; + if (this.#deviceManager == undefined) { + this.callback(); + return; + } + var list = this.#deviceManager.getTrustedDeviceListSync(); + if (typeof (list) != 'undefined' && typeof (list.length) != 'undefined') { + this.deviceList = list; + } + this.callback(); + + this.#deviceManager.on('deviceStateChange', (data) => { + switch (data.action) { + case 0: + this.deviceList[this.deviceList.length] = data.device; + this.callback(); + if (this.authCallback != null) { + this.authCallback(); + this.authCallback = null; + } + break; + case 2: + if (this.deviceList.length > 0) { + for (var i = 0; i < this.deviceList.length; i++) { + if (this.deviceList[i].deviceId === data.device.deviceId) { + this.deviceList[i] = data.device; + break; + } + } + } + this.callback(); + break; + case 1: + if (this.deviceList.length > 0) { + var list = []; + for (var i = 0; i < this.deviceList.length; i++) { + if (this.deviceList[i].deviceId != data.device.deviceId) { + list[i] = data.device; + } + } + this.deviceList = list; + } + this.callback(); + break; + default: + break; + } + }); + this.#deviceManager.on('deviceFound', (data) => { + for (let i = 0; i < this.discoverList.length; i++) { + if (that.discoverList[i].deviceId === data.device.deviceId) { + return; + } + } + this.discoverList[this.discoverList.length] = data.device; + this.callback(); + }); + this.#deviceManager.on('discoverFail', (data) => { + }); + this.#deviceManager.on('serviceDie', () => { + }); + + SUBSCRIBE_ID = Math.floor(65536 * Math.random()); + var info = { + subscribeId: SUBSCRIBE_ID, + mode: 0xAA, + medium: 2, + freq: 2, + isSameAccount: false, + isWakeRemote: true, + capability: 0 + }; + this.#deviceManager.startDeviceDiscovery(info); + } + + authDevice(deviceInfo, callback){ + let extraInfo = { + "targetPkgName": 'com.example.tictactoegame', + "appName": 'com.example.tictactoegame', + "appDescription": 'com.example.tictactoegame', + "business": '0' + }; + let authParam = { + "authType": 1, + "appIcon": '', + "appThumbnail": '', + "extraInfo": extraInfo + }; + this.#deviceManager.authenticateDevice(deviceInfo, authParam, (err, data) => { + if (err) { + this.authCallback = null; + } else { + this.authCallback = callback; + } + }); + } + + unregisterDeviceListCallback() { + this.#deviceManager.stopDeviceDiscovery(SUBSCRIBE_ID); + this.#deviceManager.off('deviceStateChange'); + this.#deviceManager.off('deviceFound'); + this.#deviceManager.off('discoverFail'); + this.#deviceManager.off('serviceDie'); + this.deviceList = []; + } +} \ No newline at end of file diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/element/string.json b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..2e61d88d4bc1ab0d8e9b8057a965c2be5336012c --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/element/string.json @@ -0,0 +1,12 @@ +{ + "string": [ + { + "name": "entry_MainAbility", + "value": "TicTacToeGame" + }, + { + "name": "description_mainability", + "value": "ETS_Empty Ability" + } + ] +} \ No newline at end of file diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/battles.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/battles.png new file mode 100644 index 0000000000000000000000000000000000000000..74361a222b37c3e42bfd7b46fd3b730fa723fa1d Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/battles.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/bg.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..99b4e0663e2f8ffe3f7a014b19e222d85d179dde Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/bg.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/bg_start_game.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/bg_start_game.png new file mode 100644 index 0000000000000000000000000000000000000000..600bbb10cbb7177c03c99881ce1e6d233e93d954 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/bg_start_game.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/circled.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/circled.png new file mode 100644 index 0000000000000000000000000000000000000000..8e6f4a392edcd07679bb49f7a2042d248bc8b694 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/circled.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/fork.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/fork.png new file mode 100644 index 0000000000000000000000000000000000000000..03f33376c29fdc41bfdb92cacfe52a0bc5ccff6f Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/fork.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/game_fail.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/game_fail.png new file mode 100644 index 0000000000000000000000000000000000000000..f46cfaa5e53f446bacd45de736a58f56691e9f40 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/game_fail.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/game_tie.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/game_tie.png new file mode 100644 index 0000000000000000000000000000000000000000..80bdad27b7b4f448bcd5bc36fc467229945d08ce Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/game_tie.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/game_win.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/game_win.png new file mode 100644 index 0000000000000000000000000000000000000000..e1336c4ce8169cb54478908675a4d79166628f86 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/game_win.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/icon.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ece0bc59e9b6888aac7b10b5d51c2068186519d0 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/icon.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/littleBK.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/littleBK.png new file mode 100644 index 0000000000000000000000000000000000000000..d5adb49680753af804ef47c2b18f41e62ab13606 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/littleBK.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_circled.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_circled.png new file mode 100644 index 0000000000000000000000000000000000000000..224d3cd1a7bb9c4d67f88bb7d0f71cde809d564d Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_circled.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_circled_current.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_circled_current.png new file mode 100644 index 0000000000000000000000000000000000000000..f4e6b986c1d0645c49687667a387f8d010378de0 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_circled_current.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_circled_me.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_circled_me.png new file mode 100644 index 0000000000000000000000000000000000000000..573a62750381eeb915cd58d803d087d215f2f413 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_circled_me.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_fork.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_fork.png new file mode 100644 index 0000000000000000000000000000000000000000..8517fe04d19457c5ddad9f8550040dd669b59c7c Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_fork.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_fork_current.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_fork_current.png new file mode 100644 index 0000000000000000000000000000000000000000..806c7755d684f6637d65672f75dde7a06eb0dd6e Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_fork_current.png differ diff --git a/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_fork_me.png b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_fork_me.png new file mode 100644 index 0000000000000000000000000000000000000000..4fbdd954d4278c678ea124df37dbdc40bfc550d2 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/entry/src/main/resources/base/media/user_fork_me.png differ diff --git a/FA/Entertainment/TicTacToeGame/gradle.properties b/FA/Entertainment/TicTacToeGame/gradle.properties new file mode 100644 index 0000000000000000000000000000000000000000..3966a047e62927da1d39dba17e4d7353d8576f43 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/gradle.properties @@ -0,0 +1,13 @@ +# Project-wide Gradle settings. +# IDE (e.g. DevEco Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# If the Chinese output is garbled, please configure the following parameter. +# This function is enabled by default when the DevEco Studio builds the hap/app,if you need disable gradle parallel,you should set org.gradle.parallel false. +# more information see https://docs.gradle.org/current/userguide/performance.html +# org.gradle.parallel=false +# org.gradle.jvmargs=-Dfile.encoding=GBK \ No newline at end of file diff --git a/FA/Entertainment/TicTacToeGame/gradle/wrapper/gradle-wrapper.jar b/FA/Entertainment/TicTacToeGame/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/gradle/wrapper/gradle-wrapper.jar differ diff --git a/FA/Entertainment/TicTacToeGame/gradle/wrapper/gradle-wrapper.properties b/FA/Entertainment/TicTacToeGame/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..f59159e865d4b59feb1b8c44b001f62fc5d58df4 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://repo.huaweicloud.com/gradle/gradle-6.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/FA/Entertainment/TicTacToeGame/gradlew b/FA/Entertainment/TicTacToeGame/gradlew new file mode 100644 index 0000000000000000000000000000000000000000..2fe81a7d95e4f9ad2c9b2a046707d36ceb3980b3 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/FA/Entertainment/TicTacToeGame/gradlew.bat b/FA/Entertainment/TicTacToeGame/gradlew.bat new file mode 100644 index 0000000000000000000000000000000000000000..62bd9b9ccefea2b65ae41e5d9a545e2021b90a1d --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/FA/Entertainment/TicTacToeGame/package.json b/FA/Entertainment/TicTacToeGame/package.json new file mode 100644 index 0000000000000000000000000000000000000000..69a88e3b65423624fe7ea8b0f8beefcc62cc3d5f --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/package.json @@ -0,0 +1 @@ +{} diff --git a/FA/Entertainment/TicTacToeGame/quick_develop.md b/FA/Entertainment/TicTacToeGame/quick_develop.md new file mode 100644 index 0000000000000000000000000000000000000000..e4d35f41a9341e2008709203a9bbaf5140d908c9 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/quick_develop.md @@ -0,0 +1,496 @@ +# 井字过三关小游戏 + +### 简介 + +Demo基于Open Harmony系统使用ETS语言进行编写,本Demo主要通过设备认证、分布式拉起、分布式数据管理等功能来实现。 + + + +#### 应用效果 + +1. ##### 设备认证,开始游戏前,如果是陌生设备需要先通过认证 + + ![deviceAuthentication](./resource/deviceAuthentication.gif) + +2. ##### 开始游戏,如果设备之前认证过了,可以直接开始游戏 + + ![startGame](./resource/startGame.gif) + + + + + +### 开发步骤 + +#### 1.新建Openharmony ETS工程 + +在DevEco Studio中点击File -> New Project ->[Standard]Empty Ability->Next,Language 选择ETS语言,最后点击Finish即创建成功。 + + ![1](./resource/1.png) + + + +#### 2.界面代码编写 + +##### 1) 开始界面 + + ![2](./resource/2.png) + + + + + +在入口组件(@Entry修饰的组件)中绘制布局,该布局使用Stack容器,展示文字,图片的堆叠效果,在容器中依次填入[Image](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/ts-based-declarative-development-paradigm/ts-basic-components-image.md),[Text](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/ts-based-declarative-development-paradigm/ts-basic-components-text.md),[Image](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/ts-based-declarative-development-paradigm/ts-basic-components-image.md),[Butto](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/ts-based-declarative-development-paradigm/ts-basic-components-button.md),[Text](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/ts-based-declarative-development-paradigm/ts-basic-components-text.md)组件,具体代码如下 + +``` +build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Stack() { + Image($r("app.media.bg")) + Column() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Text('井字游戏大作战').fontSize(30).textAlign(TextAlign.Center).fontColor(Color.White) + }.height('10%') + + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { + Stack() { + Image($r("app.media.bg_start_game")) + Column() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Image($r("app.media.battles")).height(100).width(100) + }.height('50%') + + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('开始游戏').height(50).width(200).backgroundColor('#32CD99').fontSize(20).onClick(() => { + this.startDialog.open() + }) + Text("").height(20) + Text('游戏规则').fontSize(20).fontColor('#FF00FF').height(60).decoration({ type: TextDecorationType.Underline, color: Color.Red }).onClick(() => { + this.rulesDialog.open() + }) + }.height('40%') + } + } + }.height('50%').width('90%') + + Flex() { + }.height('30%') + } + } + }.width('100%').height('100%') + } +``` + + + +自定义弹窗 + +1.点击开始按钮的弹窗如下图所示,窗口从上到下由[Text](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/ts-based-declarative-development-paradigm/ts-basic-components-text.md),[List](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/ts-based-declarative-development-paradigm/ts-container-list.md),[Button](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/ts-based-declarative-development-paradigm/ts-basic-components-button.md)组成,List中的子元素由Text和Radio组成,以下代码的省略号表示非UI相关的逻辑代码,具体实现参考源代码 + + ![3](./resource/3.png) + +``` +@CustomDialog +struct gameStart { + build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + //顶部标题 + Text('发现以下在线设备').fontColor(Color.Black).fontSize(30) + }.width('100%').height('20%') + + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { + //使用List容器动态加载在线设备 + List() { + ForEach(this.deviceName, (item) => { + ListItem() { + Row() { + //Text组件显示设备名 + Text(item.deviceName).width('80%').fontSize(30).fontColor(Color.Black) + //Radio组件显示单选框 + Radio({ value: '' }).checked(this.check[item.id]).onChange(() => { + //这里保证List里面点击了多个Radio组件时,只有当前点击的为选中状态 + for (let i = 0; i < this.check.length; i++) { + this.check[i] = false + } + this.check[item.id] = true + }) + } + } + }, item => item.id) + } + .height('80%') + + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('确定').width(200).height(50).fontSize(30).onClick(() => { + //...... + this.controller.close() + }) + }.height('30%') + + }.width('100%').height('80%') + }.height('100%').width('100%') + } +} +``` + + + +点击游戏规则的弹窗由Text,Text,button组成,具体实现可以参考弹窗1,和源代码 + + + +##### 2) 棋盘界面 + + ![4](./resource/4.png) + + + +棋盘界面由状态栏(左上图标表示当前由X或者O执行,右上表示自己本局使用X或者O),九宫格棋盘([Grid](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/ts-based-declarative-development-paradigm/ts-container-grid.md)组件绘制),button按钮组成。 + +``` +build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Row({ useAlign: HorizontalAlign.Start }) { + //左上图标 + if (this.selfChess === EMPTY) { + Image(this.current_X).height(150).width('30%') + } else if (this.curChess === CIRCLE) { + Image(this.current_O).height(150).width('30%') + } else { + Image(this.current_X).height(150).width('30%') + } + //填充空格 + Text("").width('40%') + //右上图标 + if (this.selfChess === EMPTY) { + Image(this.whiteBg).height(150).width('30%') + } else { + Image(this.selfChess === FORK ? this.self_X : this.self_O).height(150).width('30%') + } + }.height('30%') + Flex() { + //堆叠容器 + Stack() { + //绘制九宫格 + Grid() { + ForEach(this.gridVis, (item) => { + GridItem() { + //每个子格子当前需要填充的图片 + if (item.value === FORK) { + Image(this.fork).backgroundColor(Color.White) + } else if (item.value === CIRCLE) { + Image(this.circle).backgroundColor(Color.White) + } else { + Image(this.whiteBg) + } + }.onClick(() => { + //点触格子触发事件 + this.dealPoint(item) + }) + }, item => item.id) + }.columnsTemplate('1fr 1fr 1fr').rowsTemplate('1fr 1fr 1fr').columnsGap(3).rowsGap(3).align(Alignment.Center).alignSelf(ItemAlign.Center).backgroundColor('#9F5F9F') + + //根据胜负状态显示图片 + if (result === YOU_WIN) { + Image($r("app.media.game_win")).width(224).height(224) + } else if (result === YOU_LOSE) { + Image($r("app.media.game_fail")).width(224).height(224) + } else if (result === TIE) { + Image($r("app.media.game_tie")).width(224).height(224) + } + } + }.height('50%') + Row({ useAlign: HorizontalAlign.Center }) { + Button('重新开始').width(200).height(50).fontSize(30).backgroundColor('#8E6B23').onClick(() => { + this.initGame() + }) + }.height('20%') + } + } +``` + + + +#### 3.设备认证 + +设备认证是依赖[DeviceManager](https://gitee.com/openharmony/device_manager)组件来实现的,详细代码参考源码RemoteDeviceModel.ets + +1.创建DeviceManager实例 + +``` + registerDeviceListCallback(callback) { + if (typeof (this.#deviceManager) === 'undefined') { + deviceManager.createDeviceManager('com.example.tictactoegame', (error, value) => { + if (error) return + this.#deviceManager = value; + this.registerDeviceListCallback_(callback); + }); + } else { + this.registerDeviceListCallback_(callback); + } + } +``` + +2.查询可信设备列表 + +``` + var list = this.#deviceManager.getTrustedDeviceListSync(); + if (typeof (list) != 'undefined' && typeof (list.length) != 'undefined') { + this.deviceList = list; + } +``` + +3.注册设备上下线监听 + +``` +this.#deviceManager.on('deviceStateChange', (data) => { + switch (data.action) { + case 0: + this.deviceList[this.deviceList.length] = data.device; + this.callback(); + if (this.authCallback != null) { + this.authCallback(); + this.authCallback = null; + } + break; + case 2: + if (this.deviceList.length > 0) { + for (var i = 0; i < this.deviceList.length; i++) { + if (this.deviceList[i].deviceId === data.device.deviceId) { + this.deviceList[i] = data.device; + break; + } + } + } + this.callback(); + break; + case 1: + if (this.deviceList.length > 0) { + var list = []; + for (var i = 0; i < this.deviceList.length; i++) { + if (this.deviceList[i].deviceId != data.device.deviceId) { + list[i] = data.device; + } + } + this.deviceList = list; + } + this.callback(); + break; + default: + break; + } + }); +``` + +4.设备发现 + +``` +this.#deviceManager.on('deviceFound', (data) => { + for (let i = 0; i < this.discoverList.length; i++) { + if (that.discoverList[i].deviceId === data.device.deviceId) { + return; + } + } + this.discoverList[this.discoverList.length] = data.device; + this.callback(); + }); +``` + +5.设备认证 + +``` +authDevice(deviceInfo, callback){ + let extraInfo = { + "targetPkgName": 'com.example.tictactoegame', + "appName": 'com.example.tictactoegame', + "appDescription": 'com.example.tictactoegame', + "business": '0' + }; + let authParam = { + "authType": 1, + "appIcon": '', + "appThumbnail": '', + "extraInfo": extraInfo + }; + this.#deviceManager.authenticateDevice(deviceInfo, authParam, (err, data) => { + if (err) { + this.authCallback = null; + } else { + this.authCallback = callback; + } + }); + } +``` + +#### 4.数据管理 + +[分布式数据管理](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/apis/js-apis-distributed-data.md)依赖@ohos.data.distributedData模块实现,详细参考源码RemoteDataManager.ets + +1.导入该模块 + +``` +import factory from '@ohos.data.distributedData'; +``` + +2.创建KVManager实例,用于管理数据库对象 + +``` + registerDataListCallback(callback) { + let that = this + if (this.kvManager == null) { + try { + const config = { + userInfo: { + userId: '0', + userType: 0 + }, + bundleName: 'com.example.tictactoegame' + } + factory.createKVManager(config).then((manager) => { + that.kvManager = manager + that.registerDataListCallback_(callback) + }).catch((err) => { + }) + } catch (e) { + } + } else { + this.registerDataListCallback_(callback) + } + } +``` + +3.创建并获取KVStore数据库 + +``` +registerDataListCallback_(callback) { + let that = this + if (that.kvManager == null) { + callback() + return + } + if (that.kvStore == null) { + try { + let options = + { + createIfMissing: true, + encrypt: false, + backup: false, + autoSync: true, + kvStoreType: 1, + securityLevel: 3 + } + this.kvManager.getKVStore(this.STORE_ID, options).then((store) => { + that.kvStore = store + that._registerDataListCallback_(callback) + }).catch((err) => { + }) + } catch (e) { + } + } else { + this._registerDataListCallback_(callback) + } + } +``` + +4.订阅指定类型的数据变更通知 + +``` + _registerDataListCallback_(callback) { + let that = this + if (that.kvManager == null) { + callback() + return + } + this.kvStore.on('dataChange', 1, function(data) { + if (data) { + that.arr = data.updateEntries + callback() + } + }) + } +``` + +5.添加指定类型键值对到数据库 + +``` + dataChange(key, value) { + let that = this + try { + that.kvStore.put(JSON.stringify(key), JSON.stringify(value)).then((data) => { + }).catch((err) => { + prompt.showToast({message:'put err:'+JSON.stringify(value)}) + }) + + } catch (e) { + } + } +``` + + + +#### 5.远程拉起设备app + +使用[FeatureAbility](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/js-reference/apis/js-apis-featureAbility.md)模块的startAbility接口拉起远程设备app + +``` + startAbilityContinuation(deviceId) { + let wantValue = { + bundleName: 'com.example.tictactoegame', + abilityName: 'com.example.tictactoegame.MainAbility', + deviceId: deviceId, + parameters: { + uri: 'pages/Fight' + } + }; + featureAbility.startAbility({ want: wantValue }).then(() => { + router.replace({ uri: 'pages/Fight' }) + }); + } +``` + + + +#### 6.棋局胜负判定 + +对于棋盘中的任一个格子只存在已落子和未落子两种状态,我们把这两种状态定义为1和0,则九宫格可以看成是一个只有9位的二进制数,胜利状态有八种情况如下,详细代码参考源码Chess.ets: + +``` +0b111000000 +0b100100100 +0b100010001 +0b010010010 +0b001001001 +0b000111000 +0b000000111 +0b001010100 +``` + +胜负判定实现如下 + +``` + isWin() { + let x = this.result_X + let o = this.result_O + if (448 == (x & 448) || 292 == (x & 292) || 273 == (x & 273) || 146 == (x & 146) || 73 == (x & 73) || 56 == (x & 56) || 7 == (x & 7) || 84 == (x & 84)){ + this.allIsTrue() + return FORK + } + if (448 == (o & 448) || 292 == (o & 292) || 273 == (o & 273) || 146 == (o & 146) || 73 == (o & 73) || 56 == (o & 56) || 7 == (o & 7) || 84 == (o & 84)) { + this.allIsTrue() + return CIRCLE + } + let res = 0; + for (let i = 0; i < 9; i++) { + if (this.used[i] == false) { + res = 1 + break + } + } + if (res === 1) { + return CONTINUE + } + return EMPTY + } +``` + diff --git a/FA/Entertainment/TicTacToeGame/resource/1.png b/FA/Entertainment/TicTacToeGame/resource/1.png new file mode 100644 index 0000000000000000000000000000000000000000..feec26aae6061c3662c0592d845885e28d0ce5bb Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/resource/1.png differ diff --git a/FA/Entertainment/TicTacToeGame/resource/2.png b/FA/Entertainment/TicTacToeGame/resource/2.png new file mode 100644 index 0000000000000000000000000000000000000000..f4113016fafbe7fdc3d8815857e938ceaeac7eaf Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/resource/2.png differ diff --git a/FA/Entertainment/TicTacToeGame/resource/3.png b/FA/Entertainment/TicTacToeGame/resource/3.png new file mode 100644 index 0000000000000000000000000000000000000000..700e276bf135550044c1890d86da67c848ac2fc4 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/resource/3.png differ diff --git a/FA/Entertainment/TicTacToeGame/resource/4.png b/FA/Entertainment/TicTacToeGame/resource/4.png new file mode 100644 index 0000000000000000000000000000000000000000..94fd20d6aaec2f5de30ff05cf1b1396fec7693a1 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/resource/4.png differ diff --git a/FA/Entertainment/TicTacToeGame/resource/deviceAuthentication.gif b/FA/Entertainment/TicTacToeGame/resource/deviceAuthentication.gif new file mode 100644 index 0000000000000000000000000000000000000000..36b21b16ca145f873ca2abe4da311d70dd429597 Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/resource/deviceAuthentication.gif differ diff --git a/FA/Entertainment/TicTacToeGame/resource/startGame.gif b/FA/Entertainment/TicTacToeGame/resource/startGame.gif new file mode 100644 index 0000000000000000000000000000000000000000..97ce40e0c442275ce7c66535d1b8729d7c8d1cdb Binary files /dev/null and b/FA/Entertainment/TicTacToeGame/resource/startGame.gif differ diff --git a/FA/Entertainment/TicTacToeGame/settings.gradle b/FA/Entertainment/TicTacToeGame/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..28d595f2fba0d06b2025da200383d15f87c4e9f0 --- /dev/null +++ b/FA/Entertainment/TicTacToeGame/settings.gradle @@ -0,0 +1 @@ +include ':entry'