├── settings.gradle
├── app
├── src
│ └── main
│ │ ├── assets
│ │ └── Symbola.ttf
│ │ ├── res
│ │ ├── drawable-hdpi
│ │ │ └── logo_2048.png
│ │ ├── drawable-mdpi
│ │ │ └── logo_2048.png
│ │ ├── drawable-xhdpi
│ │ │ └── logo_2048.png
│ │ ├── drawable-xxhdpi
│ │ │ └── logo_2048.png
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── arrays.xml
│ │ ├── xml
│ │ │ ├── backup_descriptor.xml
│ │ │ └── settings.xml
│ │ ├── drawable
│ │ │ ├── cell_rectangle_128.xml
│ │ │ ├── cell_rectangle_16.xml
│ │ │ ├── cell_rectangle_2.xml
│ │ │ ├── cell_rectangle_32.xml
│ │ │ ├── cell_rectangle_4.xml
│ │ │ ├── cell_rectangle_64.xml
│ │ │ ├── cell_rectangle_8.xml
│ │ │ ├── background_rectangle.xml
│ │ │ ├── cell_rectangle.xml
│ │ │ ├── cell_rectangle_256.xml
│ │ │ ├── cell_rectangle_512.xml
│ │ │ ├── fade_rectangle.xml
│ │ │ ├── light_up_rectangle.xml
│ │ │ ├── cell_rectangle_1024.xml
│ │ │ └── cell_rectangle_2048.xml
│ │ ├── layout
│ │ │ └── activity_my_settings.xml
│ │ ├── menu
│ │ │ └── main.xml
│ │ └── values-zh
│ │ │ └── strings.xml
│ │ ├── java
│ │ └── com
│ │ │ └── osfans
│ │ │ └── android2048
│ │ │ ├── SettingsActivity.java
│ │ │ ├── Cell.java
│ │ │ ├── AnimationCell.java
│ │ │ ├── Tile.java
│ │ │ ├── SettingsProvider.java
│ │ │ ├── AnimationGrid.java
│ │ │ ├── Grid.java
│ │ │ ├── InputListener.java
│ │ │ ├── SettingsFragment.java
│ │ │ ├── MainActivity.java
│ │ │ ├── MainGame.java
│ │ │ ├── AI.java
│ │ │ └── MainView.java
│ │ └── AndroidManifest.xml
└── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── README.md
├── gradlew.bat
├── gradlew
└── LICENSE
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app/src/main/assets/Symbola.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osfans/android2048/HEAD/app/src/main/assets/Symbola.ttf
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osfans/android2048/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/logo_2048.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osfans/android2048/HEAD/app/src/main/res/drawable-hdpi/logo_2048.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/logo_2048.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osfans/android2048/HEAD/app/src/main/res/drawable-mdpi/logo_2048.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/logo_2048.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osfans/android2048/HEAD/app/src/main/res/drawable-xhdpi/logo_2048.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/logo_2048.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osfans/android2048/HEAD/app/src/main/res/drawable-xxhdpi/logo_2048.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #f9f6f2
4 | #776e65
5 | #d6cdc4
6 | #faf8ef
7 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_descriptor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | *.iml
17 | .gradle/
18 | .idea/
19 | build/
20 |
21 | # Local configuration file (sdk path, etc)
22 | local.properties
23 | gradle.properties
24 | *.keystore
25 |
26 | # Proguard folder generated by Eclipse
27 | proguard/
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | import android.os.Bundle;
4 | import androidx.appcompat.app.AppCompatActivity;
5 |
6 |
7 | public class SettingsActivity extends AppCompatActivity {
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_my_settings);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle_128.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle_16.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle_32.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle_64.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle_8.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_rectangle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle_256.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle_512.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/fade_rectangle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/light_up_rectangle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle_1024.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cell_rectangle_2048.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 2048方言版
2 | ====
3 |
4 | ## 簡介
5 | - 源於[fengmoxi的2048](https://github.com/fengmoxi/2048_android)
6 | - 系統要求:>=Android4.0
7 | - 階數:3~7
8 | - 任務:128~2147483648
9 |
10 | ## 多種自定義版
11 | - 原版(2048)
12 | - 揚州話(2048)
13 | - 蘇州閒話(2048)
14 | - 麻將字牌(128)
15 | - 八卦版(256)
16 | - 麻將萬字(512)
17 | - 天干版(1024)
18 | - 地支版(4096)
19 | - 12生肖(4096)
20 | - 12星座(4096)
21 | - 中國朝代(8192)
22 | - 江蘇城市(8192)
23 | - 撲克版(16384)
24 | - 五行版(32768)
25 | - 16韻攝(65536)
26 | - 24節氣(16777216)
27 | - 終極版(2147483648)
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/Cell.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | class Cell {
4 | boolean marked = false;
5 | private int x;
6 | private int y;
7 |
8 | Cell(int x, int y) {
9 | this.x = x;
10 | this.y = y;
11 | }
12 |
13 | int getX() {
14 | return this.x;
15 | }
16 |
17 | void setX(int x) {
18 | this.x = x;
19 | }
20 |
21 | int getY() {
22 | return this.y;
23 | }
24 |
25 | void setY(int y) {
26 | this.y = y;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_my_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/AnimationCell.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | class AnimationCell extends Cell {
4 | final int[] extras;
5 | private final int animationType;
6 | private long timeElapsed;
7 | private final long animationTime;
8 | private final long delayTime;
9 |
10 | AnimationCell(int x, int y, int animationType, long length, long delay, int[] extras) {
11 | super(x, y);
12 | this.animationType = animationType;
13 | animationTime = length;
14 | delayTime = delay;
15 | this.extras = extras;
16 | }
17 |
18 | int getAnimationType() {
19 | return animationType;
20 | }
21 |
22 | void tick(long timeElapsed) {
23 | this.timeElapsed = this.timeElapsed + timeElapsed;
24 | }
25 |
26 | boolean animationDone() {
27 | return animationTime + delayTime < timeElapsed;
28 | }
29 |
30 | double getPercentageDone() {
31 | return Math.max(0, 1.0 * (timeElapsed - delayTime) / animationTime);
32 | }
33 |
34 | boolean isActive() {
35 | return (timeElapsed >= delayTime);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/Tile.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | class Tile extends Cell {
4 | private final int value;
5 | //private Cell previousPosition = null;
6 | private Tile[] mergedFrom = null;
7 |
8 | Tile(int x, int y, int value) {
9 | super(x, y);
10 | this.value = value;
11 | }
12 |
13 | Tile(Cell cell, int value) {
14 | super(cell.getX(), cell.getY());
15 | this.value = value;
16 | }
17 |
18 | /*
19 | void savePosition() {
20 | previousPosition = new Cell(this.getX(), this.getY());
21 | }
22 | */
23 |
24 | void updatePosition(Cell cell) {
25 | this.setX(cell.getX());
26 | this.setY(cell.getY());
27 | }
28 |
29 | int getValue() {
30 | return this.value;
31 | }
32 |
33 | /*
34 | public void setValue(int value) {
35 | this.value = value;
36 | }
37 | */
38 |
39 | Tile[] getMergedFrom() {
40 | return mergedFrom;
41 | }
42 |
43 | void setMergedFrom(Tile[] tile) {
44 | mergedFrom = tile;
45 | }
46 |
47 | /*
48 | public Cell getPreviousPosition() {
49 | return previousPosition;
50 | }
51 | */
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2048方言版
4 | 紀錄
5 | 得分
6 | 滑動合併相同的滑塊
7 | 触摸空白方块可添加滑块
8 | 勝利
9 | 結束
10 | 反悔
11 | 設置
12 | 託管
13 | 新建
14 | 靈敏度
15 | 高
16 | 中
17 | 低
18 | 版本
19 | 定製
20 | 反轉
21 | 序號
22 | 階數
23 | 不顯示
24 | 系統字體
25 | 開源
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2048 Variants
4 | HIGH SCORE
5 | SCORE
6 | Swipe to move.
7 | Tap on empty cells to add one.
8 | You Win
9 | Game Over
10 | Undo
11 | Settings
12 | Auto Run
13 | New Game
14 | Sensitivity
15 | High
16 | Medium
17 | Low
18 | Variety
19 | Custom
20 | Inverse Mode
21 | Show Order
22 | None
23 | Row Number
24 | System Font
25 | Project Homepage
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/SettingsProvider.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | class SettingsProvider {
7 | private static final String KEY_PREFERENCES = "com.osfans.android2048_preferences";
8 |
9 | static final String KEY_SENSITIVITY = "settings_sensitivity";
10 | static final String KEY_ORDER = "settings_order";
11 | static final String KEY_ROWS = "settings_rows";
12 | static final String KEY_VARIETY = "settings_variety";
13 | static final String KEY_INVERSE_MODE = "settings_inverse_mode";
14 | static final String KEY_SYSTEM_FONT = "settings_system_font";
15 | static final String KEY_CUSTOM_VARIETY = "settings_custom_variety";
16 |
17 | private static SharedPreferences prefs;
18 |
19 | static void initPreferences(Context context) {
20 | prefs = context.getSharedPreferences(KEY_PREFERENCES, Context.MODE_MULTI_PROCESS);
21 | }
22 |
23 | static int getInt(String key, String defaultValue) {
24 | return Integer.parseInt(prefs.getString(key, defaultValue));
25 | }
26 |
27 | static boolean getBoolean(String key) {
28 | return prefs.getBoolean(key, false);
29 | }
30 |
31 | static String getString(String key, String defaultValue) {
32 | return prefs.getString(key, defaultValue);
33 | }
34 |
35 | static void putBoolean(String key, boolean value) {
36 | prefs.edit().putBoolean(key, value).apply();
37 | }
38 |
39 | static void putString(String key, String value) {
40 | prefs.edit().putString(key, value).apply();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | def tagCmd = 'git describe --tags'
4 | def tag = tagCmd.execute().text.trim()
5 | def date = new Date().format('yyyyMMdd').toInteger()
6 |
7 | android {
8 | compileSdkVersion 30
9 |
10 | defaultConfig {
11 | applicationId "com.osfans.android2048"
12 | minSdkVersion 14
13 | targetSdkVersion 30
14 | versionCode date
15 | versionName "$tag-$date"
16 | }
17 |
18 | signingConfigs {
19 | release {
20 | }
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | //proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-android.txt'
27 | signingConfig signingConfigs.release
28 | }
29 | }
30 |
31 | compileOptions {
32 | sourceCompatibility JavaVersion.VERSION_1_8
33 | targetCompatibility JavaVersion.VERSION_1_8
34 | }
35 |
36 | def propsFile = rootProject.file('gradle.properties')
37 | //store release config in gradle.properties
38 | def configName = 'release'
39 |
40 | if (propsFile.exists() && android.signingConfigs.hasProperty(configName)) {
41 | def props = new Properties()
42 | props.load(new FileInputStream(propsFile))
43 | if (props != null && props.containsKey('storeFile')) {
44 | android.signingConfigs[configName].storeFile = rootProject.file(props['storeFile'])
45 | android.signingConfigs[configName].storePassword = props['storePassword']
46 | android.signingConfigs[configName].keyAlias = props['keyAlias']
47 | android.signingConfigs[configName].keyPassword = props['keyPassword']
48 | }
49 | }
50 | }
51 |
52 | dependencies {
53 | implementation 'androidx.preference:preference:1.1.0'
54 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
55 | implementation 'androidx.appcompat:appcompat:1.1.0'
56 | implementation 'com.google.android.material:material:1.0.0'
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
22 |
23 |
29 |
30 |
34 |
35 |
41 |
42 |
46 |
47 |
48 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/AnimationGrid.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | import java.util.ArrayList;
4 |
5 |
6 | class AnimationGrid {
7 | private final ArrayList[][] field;
8 | final ArrayList globalAnimation = new ArrayList<>();
9 | private int activeAnimations = 0;
10 | private boolean oneMoreFrame = false;
11 |
12 | AnimationGrid(int x, int y) {
13 | field = new ArrayList[x][y];
14 |
15 | for (int xx = 0; xx < x; xx++) {
16 | for (int yy = 0; yy < y; yy++) {
17 | field[xx][yy] = new ArrayList<>();
18 | }
19 | }
20 | }
21 |
22 | void startAnimation(int x, int y, int animationType, long length, long delay, int[] extras) {
23 | AnimationCell animationToAdd = new AnimationCell(x, y, animationType, length, delay, extras);
24 | if (x == -1 && y == -1) {
25 | globalAnimation.add(animationToAdd);
26 | } else {
27 | field[x][y].add(animationToAdd);
28 | }
29 | activeAnimations = activeAnimations + 1;
30 | }
31 |
32 | void tickAll(long timeElapsed) {
33 | ArrayList cancelledAnimations = new ArrayList<>();
34 | for (AnimationCell animation : globalAnimation) {
35 | animation.tick(timeElapsed);
36 | if (animation.animationDone()) {
37 | cancelledAnimations.add(animation);
38 | activeAnimations = activeAnimations - 1;
39 | }
40 | }
41 |
42 | for (ArrayList[] array : field) {
43 | for (ArrayList list : array) {
44 | for (AnimationCell animation : list) {
45 | animation.tick(timeElapsed);
46 | if (animation.animationDone()) {
47 | cancelledAnimations.add(animation);
48 | activeAnimations = activeAnimations - 1;
49 | }
50 | }
51 | }
52 | }
53 |
54 | for (AnimationCell animation : cancelledAnimations) {
55 | cancelAnimation(animation);
56 | }
57 | }
58 |
59 | boolean isAnimationActive() {
60 | if (activeAnimations != 0) {
61 | oneMoreFrame = true;
62 | return true;
63 | } else if (oneMoreFrame) {
64 | oneMoreFrame = false;
65 | return true;
66 | } else {
67 | return false;
68 | }
69 | }
70 |
71 | ArrayList getAnimationCell(int x, int y) {
72 | return field[x][y];
73 | }
74 |
75 | private void cancelAnimation(AnimationCell animation) {
76 | if (animation.getX() == -1 && animation.getY() == -1) {
77 | globalAnimation.remove(animation);
78 | } else {
79 | field[animation.getX()][animation.getY()].remove(animation);
80 | }
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @string/settings_sensitivity_high
5 | - @string/settings_sensitivity_medium
6 | - @string/settings_sensitivity_low
7 |
8 |
9 | - 0
10 | - 1
11 | - 2
12 |
13 |
14 | - 123
15 | - ABC
16 | - @string/settings_order_none
17 |
18 |
19 | - 0
20 | - 1
21 | - 2
22 |
23 |
24 | - 3
25 | - 4
26 | - 5
27 | - 6
28 | - 7
29 |
30 | 2 4 8 16 32 64 128 256 512 1024 2048
31 |
32 | - @string/original_2048
33 | - 大胖子怕熱 雞蛋怕砑 小車子怕山 破扁擔怕試 文人怕武 回子怕肉 嗝食病怕喫 壞鞋子怕拔 螃蟹怕酒 陰溝洞怕塞 乖乖隆地咚
34 | - 歪戴帽子大阿哥 纏夾二先生 橋頭三阿爹 吐血四老倌 五叔叔 六嬸嬸 七巧妹子 八阿姨 九斤老太 十伯伯 蘇州閒話
35 | - 🀀 🀁 🀂 🀃 🀄 🀅 🀆
36 | - 🀢 🀣 🀤 🀥 🀦 🀧 🀨 🀩
37 | - ☰ ☱ ☲ ☳ ☴ ☵ ☶ ☷
38 | - 🀇 🀈 🀉 🀊 🀋 🀌 🀍 🀎 🀏
39 | - 甲乙丙丁戊己庚辛壬癸
40 | - 子丑寅卯辰巳午未申酉戌亥
41 | - 🐀 🐂 🐅 🐇 🐉 🐍 🐎 🐏 🐒 🐓 🐕 🐖
42 | - ♈ ♉ ♊ ♋ ♌ ♍ ♎ ♏ ♐ ♑ ♒ ♓
43 | - 〡〢〣〤〥〦〧〨〩〸〹〺
44 | - 夏商周秦漢晉隋唐宋元明清天
45 | - 宁錫徐常蘇通連淮鹽揚鎮泰宿
46 | - 🂡 🂢 🂣 🂤 🂥 🂦 🂧 🂨 🂩 🂪 🂫 🂭 🂮 🃟
47 | - 木火土金水林炎圭鍂沝森焱垚鑫淼
48 | - 通江止遇蟹臻山效果假宕梗曾流深咸
49 | - 立春 雨水 驚蟄 春分 清明 穀雨 立夏 小滿 芒種 夏至 小暑 大暑 立秋 處暑 白露 秋分 寒露 霜降 立冬 小雪 大雪 冬至 小寒 大寒
50 | - 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152 4194304 8388608 16777216 33554432 67108864 134217728 268435456 536870912 1073741824 2147483648
51 | - @string/original_2048
52 |
53 |
54 | - 0
55 | - 1
56 | - 2
57 | - 3
58 | - 4
59 | - 5
60 | - 6
61 | - 7
62 | - 8
63 | - 9
64 | - 10
65 | - 11
66 | - 12
67 | - 13
68 | - 14
69 | - 15
70 | - 16
71 | - 17
72 | - 18
73 | - 19
74 |
75 |
76 | - 原版
77 | - 揚州話
78 | - 蘇州閒話
79 | - 麻將字牌
80 | - 麻將花牌
81 | - 八卦
82 | - 麻將萬字
83 | - 天干
84 | - 地支
85 | - 生肖
86 | - 星座
87 | - 蘇州碼子
88 | - 中國朝代
89 | - 江蘇城市
90 | - 撲克
91 | - 五行
92 | - 韻攝
93 | - 節氣
94 | - 終極版
95 | - @string/settings_custom_variety
96 |
97 | 1
98 | 1
99 | 0
100 | 4
101 |
102 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/Grid.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | import java.util.ArrayList;
4 |
5 | public class Grid {
6 |
7 | Tile[][] field;
8 | private Tile[][] lastField;
9 | boolean canRevert = false;
10 |
11 | private final int sizeX;
12 | private final int sizeY;
13 |
14 | Grid(int sizeX, int sizeY) {
15 | this.sizeX = sizeX;
16 | this.sizeY = sizeY;
17 | field = new Tile[sizeX][sizeY];
18 | lastField = new Tile[sizeX][sizeY];
19 | for (int xx = 0; xx < field.length; xx++) {
20 | for (int yy = 0; yy < field[0].length; yy++) {
21 | field[xx][yy] = null;
22 | lastField[xx][yy] = null;
23 | }
24 | }
25 | }
26 |
27 | Cell randomAvailableCell() {
28 | ArrayList availableCells = getAvailableCells();
29 | if (availableCells.size() >= 1) {
30 | return availableCells.get((int) Math.floor(Math.random() * availableCells.size()));
31 | }
32 | return null;
33 | }
34 |
35 | ArrayList getAvailableCells() {
36 | ArrayList| availableCells = new ArrayList<>();
37 | for (int xx = 0; xx < field.length; xx++) {
38 | for (int yy = 0; yy < field[0].length; yy++) {
39 | if (field[xx][yy] == null) {
40 | availableCells.add(new Cell(xx, yy));
41 | }
42 | }
43 | }
44 | return availableCells;
45 | }
46 |
47 | boolean isCellsAvailable() {
48 | return (getAvailableCells().size() >= 1);
49 | }
50 |
51 | boolean isCellAvailable(Cell cell) {
52 | return !isCellOccupied(cell);
53 | }
54 |
55 | boolean isCellOccupied(Cell cell) {
56 | return (getCellContent(cell) != null);
57 | }
58 |
59 | Tile getCellContent(Cell cell) {
60 | if (cell != null && isCellWithinBounds(cell)) {
61 | return field[cell.getX()][cell.getY()];
62 | } else {
63 | return null;
64 | }
65 | }
66 |
67 | Tile getCellContent(int x, int y) {
68 | if (isCellWithinBounds(x, y)) {
69 | return field[x][y];
70 | } else {
71 | return null;
72 | }
73 | }
74 |
75 | boolean isCellWithinBounds(Cell cell) {
76 | return 0 <= cell.getX() && cell.getX() < field.length
77 | && 0 <= cell.getY() && cell.getY() < field[0].length;
78 | }
79 |
80 | boolean isCellWithinBounds(int x, int y) {
81 | return 0 <= x && x < field.length
82 | && 0 <= y && y < field[0].length;
83 | }
84 |
85 | void insertTile(Tile tile) {
86 | field[tile.getX()][tile.getY()] = tile;
87 | }
88 |
89 | void removeTile(Tile tile) {
90 | field[tile.getX()][tile.getY()] = null;
91 | }
92 |
93 | void saveTiles() {
94 | canRevert = true;
95 |
96 | lastField = new Tile[sizeX][sizeY];
97 | for (int xx = 0; xx < field.length; xx++) {
98 | for (int yy = 0; yy < field.length; yy++) {
99 | if (field[xx][yy] == null) {
100 | lastField[xx][yy] = null;
101 | } else {
102 | lastField[xx][yy] = new Tile(xx, yy, field[xx][yy].getValue());
103 | }
104 | }
105 | }
106 | }
107 |
108 | void revertTiles() {
109 | canRevert = false;
110 |
111 | for (int xx = 0; xx < lastField.length; xx++) {
112 | for (int yy = 0; yy < lastField.length; yy++) {
113 | if (lastField[xx][yy] == null) {
114 | field[xx][yy] = null;
115 | } else {
116 | field[xx][yy] = new Tile(xx, yy, lastField[xx][yy].getValue());
117 | }
118 | }
119 | }
120 | }
121 |
122 | @Override
123 | public Grid clone() {
124 | Tile[][] newField = new Tile[sizeX][sizeY];
125 | for (int xx = 0; xx < field.length; xx++) {
126 | for (int yy = 0; yy < field.length; yy++) {
127 | if (field[xx][yy] == null) {
128 | newField[xx][yy] = null;
129 | } else {
130 | newField[xx][yy] = new Tile(xx, yy, field[xx][yy].getValue());
131 | }
132 | }
133 | }
134 |
135 | Grid newGrid = new Grid(sizeX, sizeY);
136 | newGrid.field = newField;
137 | return newGrid;
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/InputListener.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | import android.view.GestureDetector;
4 | import android.view.KeyEvent;
5 | import android.view.MotionEvent;
6 | import android.view.View;
7 |
8 | class InputListener implements View.OnTouchListener, View.OnKeyListener {
9 |
10 | private static final int SWIPE_MIN_DISTANCE = 100;
11 | private static int SWIPE_THRESHOLD_VELOCITY = 40;
12 | private static int MOVE_THRESHOLD = 250;
13 | private final MainView mView;
14 | private final GestureDetector mGestureDetector;
15 | private float x;
16 | private float y;
17 | /*
18 | private static final int RESET_STARTING = 10;
19 | private float lastDx;
20 | private float lastDy;
21 | private float previousX;
22 | private float previousY;
23 | private float startingX;
24 | private float startingY;
25 | private boolean moved = false;
26 | */
27 | private int previousDirection = 1;
28 | private int veryLastDirection = 1;
29 |
30 | InputListener(MainView view) {
31 | super();
32 | this.mView = view;
33 | mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
34 | @Override
35 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
36 | try {
37 | float fX = e1.getX() - e2.getX();
38 | float fY = e1.getY() - e2.getY();
39 | if (Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY && Math.abs(fX) + SWIPE_MIN_DISTANCE / 2 >= Math.abs(fY) && Math.abs(fX) > SWIPE_MIN_DISTANCE && Math.abs(fX) < MOVE_THRESHOLD * 2) {
40 | mView.game.move(fX > 0 ? 3 : 1);
41 | } else if (Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY && Math.abs(fY) > SWIPE_MIN_DISTANCE && Math.abs(fY) < MOVE_THRESHOLD * 2) {
42 | mView.game.move(fY > 0 ? 0 : 2);
43 | } else return false;
44 | } catch (Exception e) {
45 | e.printStackTrace();
46 | }
47 | return true;
48 | }
49 |
50 | @Override
51 | public boolean onSingleTapUp(MotionEvent event) {
52 |
53 | x = event.getX();
54 | y = event.getY();
55 | previousDirection = 1;
56 | veryLastDirection = 1;
57 | if (inRange(MainView.sXNewGame, x, MainView.sXNewGame + MainView.iconSize)
58 | && inRange(MainView.sYIcons, y, MainView.sYIcons + MainView.iconSize)) {
59 | mView.game.newGame();
60 | }
61 |
62 | if (MainView.inverseMode) {
63 | for (Cell cell : mView.game.grid.getAvailableCells()) {
64 | int xx = cell.getX();
65 | int yy = cell.getY();
66 | int sX = mView.startingX + mView.gridWidth + (mView.cellSize + mView.gridWidth) * xx;
67 | int eX = sX + mView.cellSize;
68 | int sY = mView.startingY + mView.gridWidth + (mView.cellSize + mView.gridWidth) * yy;
69 | int eY = sY + mView.cellSize;
70 |
71 | if (inRange(sX, x, eX) && inRange(sY, y, eY)) {
72 | mView.game.addRandomTile(cell);
73 | mView.invalidate();
74 | mView.startAi();
75 | break;
76 | }
77 | }
78 | }
79 | return true;
80 | }
81 | });
82 | }
83 |
84 | static void loadSensitivity() {
85 | int sensitivity = SettingsProvider.getInt(SettingsProvider.KEY_SENSITIVITY, "1");
86 | switch (sensitivity) {
87 | case 0:
88 | SWIPE_THRESHOLD_VELOCITY = 20;
89 | MOVE_THRESHOLD = 200;
90 | break;
91 | case 1:
92 | SWIPE_THRESHOLD_VELOCITY = 60;
93 | MOVE_THRESHOLD = 250;
94 | break;
95 | case 2:
96 | SWIPE_THRESHOLD_VELOCITY = 100;
97 | MOVE_THRESHOLD = 300;
98 | break;
99 | }
100 | }
101 |
102 | public boolean onTouch(View view, MotionEvent event) {
103 | view.performClick();
104 | mGestureDetector.onTouchEvent(event);
105 | return true;
106 | }
107 |
108 | @Override
109 | public boolean onKey(View view, int keyCode, KeyEvent event) {
110 | if (event.getAction() == KeyEvent.ACTION_DOWN) {
111 | switch (event.getKeyCode()) {
112 | case KeyEvent.KEYCODE_DPAD_DOWN:
113 | mView.game.move(2);
114 | return true;
115 | case KeyEvent.KEYCODE_DPAD_UP:
116 | mView.game.move(0);
117 | return true;
118 | case KeyEvent.KEYCODE_DPAD_LEFT:
119 | mView.game.move(3);
120 | return true;
121 | case KeyEvent.KEYCODE_DPAD_RIGHT:
122 | mView.game.move(1);
123 | return true;
124 | }
125 | }
126 | return false;
127 | }
128 |
129 | private boolean inRange(float left, float check, float right) {
130 | return (left <= check && check <= right);
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/SettingsFragment.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | import androidx.preference.PreferenceFragmentCompat;
4 |
5 | import android.os.Bundle;
6 |
7 | import androidx.preference.CheckBoxPreference;
8 | import androidx.preference.ListPreference;
9 | import androidx.preference.Preference;
10 |
11 |
12 | /**
13 | * A placeholder fragment containing a simple view.
14 | */
15 | public class SettingsFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceChangeListener {
16 | private Preference mCustomVariety;
17 |
18 | @Override
19 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
20 | setPreferencesFromResource(R.xml.settings, rootKey);
21 | ListPreference sensitivity = findPreference(SettingsProvider.KEY_SENSITIVITY);
22 | ListPreference order = findPreference(SettingsProvider.KEY_ORDER);
23 | ListPreference rows = findPreference(SettingsProvider.KEY_ROWS);
24 | ListPreference variety = findPreference(SettingsProvider.KEY_VARIETY);
25 | CheckBoxPreference inverse = findPreference(SettingsProvider.KEY_INVERSE_MODE);
26 | CheckBoxPreference systemFont = findPreference(SettingsProvider.KEY_SYSTEM_FONT);
27 | mCustomVariety = findPreference(SettingsProvider.KEY_CUSTOM_VARIETY);
28 |
29 | if (sensitivity != null) {
30 | int value = SettingsProvider.getInt(SettingsProvider.KEY_SENSITIVITY, getResources().getString(R.string.default_sensitivity));
31 | String[] summaries = getResources().getStringArray(R.array.settings_sensitivity_entries);
32 | sensitivity.setSummary(summaries[value]);
33 | sensitivity.setOnPreferenceChangeListener(this);
34 | }
35 | if (order != null) {
36 | int value = SettingsProvider.getInt(SettingsProvider.KEY_ORDER, getResources().getString(R.string.default_order));
37 | String[] summaries = getResources().getStringArray(R.array.settings_order_entries);
38 | order.setSummary(summaries[value]);
39 | order.setOnPreferenceChangeListener(this);
40 | }
41 | if (rows != null) {
42 | String value = SettingsProvider.getString(SettingsProvider.KEY_ROWS, getResources().getString(R.string.default_row));
43 | rows.setSummary(value);
44 | rows.setOnPreferenceChangeListener(this);
45 | }
46 | if (variety != null) {
47 | int value = SettingsProvider.getInt(SettingsProvider.KEY_VARIETY, getResources().getString(R.string.default_variety));
48 | String[] summaries = getResources().getStringArray(R.array.settings_variety_entries);
49 | variety.setSummary(summaries[value]);
50 | variety.setOnPreferenceChangeListener(this);
51 | }
52 | if (inverse != null) inverse.setOnPreferenceChangeListener(this);
53 | if (systemFont != null) systemFont.setOnPreferenceChangeListener(this);
54 | if (mCustomVariety != null) mCustomVariety.setOnPreferenceChangeListener(this);
55 | }
56 |
57 | public boolean onPreferenceChange(Preference preference, Object newValue) {
58 | String key = preference.getKey();
59 | switch (key) {
60 | case SettingsProvider.KEY_SENSITIVITY:
61 | int sensitivity = Integer.parseInt((String) newValue);
62 | String[] sensitivitySummaries = getResources().getStringArray(R.array.settings_sensitivity_entries);
63 | preference.setSummary(sensitivitySummaries[sensitivity]);
64 | SettingsProvider.putString(SettingsProvider.KEY_SENSITIVITY, (String) newValue);
65 | InputListener.loadSensitivity();
66 | break;
67 | case SettingsProvider.KEY_VARIETY:
68 | int variety = ((ListPreference)preference).findIndexOfValue((String) newValue);
69 | String[] varietySummaries = getResources().getStringArray(R.array.settings_variety_entries);
70 | mCustomVariety.setEnabled(variety == varietySummaries.length - 1);
71 | preference.setSummary(varietySummaries[variety]);
72 | SettingsProvider.putString(SettingsProvider.KEY_VARIETY, (String) newValue);
73 | MainActivity.getInstance().newGame();
74 | break;
75 | case SettingsProvider.KEY_CUSTOM_VARIETY:
76 | SettingsProvider.putString(SettingsProvider.KEY_CUSTOM_VARIETY, (String) newValue);
77 | MainActivity.getInstance().newGame();
78 | break;
79 | case SettingsProvider.KEY_INVERSE_MODE:
80 | boolean inverse = (Boolean) newValue;
81 | SettingsProvider.putBoolean(SettingsProvider.KEY_INVERSE_MODE, inverse);
82 | MainView.inverseMode = inverse;
83 | break;
84 | case SettingsProvider.KEY_SYSTEM_FONT:
85 | boolean value = (Boolean) newValue;
86 | SettingsProvider.putBoolean(SettingsProvider.KEY_SYSTEM_FONT, value);
87 | MainActivity.getInstance().newGame();
88 | break;
89 | case SettingsProvider.KEY_ORDER:
90 | int order = Integer.parseInt((String) newValue);
91 | String[] orderSummaries = getResources().getStringArray(R.array.settings_order_entries);
92 | preference.setSummary(orderSummaries[order]);
93 | SettingsProvider.putString(SettingsProvider.KEY_ORDER, (String) newValue);
94 | MainActivity.getInstance().newCell();
95 | break;
96 | case SettingsProvider.KEY_ROWS:
97 | SettingsProvider.putString(SettingsProvider.KEY_ROWS, (String) newValue);
98 | preference.setSummary((String) newValue);
99 | clearState();
100 | MainActivity.getInstance().newGame();
101 | break;
102 | default:
103 | return false;
104 | }
105 | return true;
106 | }
107 |
108 | private void clearState() {
109 | MainActivity.getInstance().getSharedPreferences("state", 0)
110 | .edit()
111 | .remove("size")
112 | .apply();
113 | MainActivity.save = false;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | import android.content.Intent;
4 | import android.content.SharedPreferences;
5 | import android.content.res.Resources;
6 | import android.os.Bundle;
7 | import android.view.Menu;
8 | import android.view.MenuInflater;
9 | import android.view.MenuItem;
10 | import android.view.WindowManager;
11 |
12 | import androidx.appcompat.app.AppCompatActivity;
13 |
14 | public class MainActivity extends AppCompatActivity {
15 |
16 | public static boolean save = true;
17 | private static MainActivity mSelf;
18 | private MainView view;
19 |
20 | public static MainActivity getInstance() {
21 | return mSelf;
22 | }
23 |
24 | public void newGame() {
25 | view = new MainView(getBaseContext());
26 |
27 | // Restore state
28 | SharedPreferences prefs = getSharedPreferences("state", 0);
29 | int size = prefs.getInt("size", 0);
30 | if (size == MainGame.numSquaresX) {
31 | Tile[][] field = view.game.grid.field;
32 | String[] saveState = new String[field[0].length];
33 | for (int xx = 0; xx < saveState.length; xx++) {
34 | saveState[xx] = prefs.getString("" + xx, "");
35 | }
36 | for (int xx = 0; xx < saveState.length; xx++) {
37 | String[] array = saveState[xx].split("\\|");
38 | for (int yy = 0; yy < array.length; yy++) {
39 | if (!array[yy].startsWith("0")) {
40 | view.game.grid.field[xx][yy] = new Tile(xx, yy, Integer.valueOf(array[yy]));
41 | } else {
42 | view.game.grid.field[xx][yy] = null;
43 | }
44 | }
45 | }
46 | view.game.score = prefs.getLong("score", 0);
47 | view.game.highScore = prefs.getLong("high score", 0);
48 | view.game.won = prefs.getBoolean("won", false);
49 | view.game.lose = prefs.getBoolean("lose", false);
50 | }
51 | setContentView(view);
52 | initTitle();
53 | }
54 |
55 | public void newCell() {
56 | view.initRectangleDrawables();
57 | }
58 |
59 | private void initTitle() {
60 | Resources resources = getResources();
61 | int i = SettingsProvider.getInt(SettingsProvider.KEY_VARIETY, resources.getString(R.string.default_variety));
62 | String[] varietySummaries = resources.getStringArray(R.array.settings_variety_entries);
63 | setTitle(varietySummaries[i]);
64 | }
65 |
66 | @Override
67 | protected void onCreate(Bundle savedInstanceState) {
68 | super.onCreate(savedInstanceState);
69 | mSelf = this;
70 | SettingsProvider.initPreferences(this);
71 | InputListener.loadSensitivity();
72 |
73 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
74 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
75 | newGame();
76 | }
77 |
78 | @Override
79 | public boolean onCreateOptionsMenu(Menu menu) {
80 | MenuInflater inflater = getMenuInflater();
81 | inflater.inflate(R.menu.main, menu);
82 | return true;
83 | }
84 |
85 | @Override
86 | public boolean onPrepareOptionsMenu(Menu menu) {
87 | menu.findItem(R.id.menu_auto_run).setCheckable(true);
88 | if (MainView.inverseMode) {
89 | menu.findItem(R.id.menu_undo).setEnabled(false);
90 | menu.findItem(R.id.menu_auto_run).setEnabled(false);
91 | } else if (view.aiRunning) {
92 | menu.findItem(R.id.menu_undo).setEnabled(false);
93 | menu.findItem(R.id.menu_auto_run).setEnabled(true);
94 | menu.findItem(R.id.menu_auto_run).setChecked(true);
95 | } else {
96 | menu.findItem(R.id.menu_undo).setEnabled(view.game.grid.canRevert);
97 | menu.findItem(R.id.menu_auto_run).setEnabled(true);
98 | menu.findItem(R.id.menu_auto_run).setChecked(false);
99 | }
100 |
101 | return true;
102 | }
103 |
104 | @Override
105 | public boolean onOptionsItemSelected(MenuItem item) {
106 | switch (item.getItemId()) {
107 | case R.id.menu_undo:
108 | view.game.revertState();
109 | return true;
110 | case R.id.menu_settings:
111 | Intent i = new Intent();
112 | i.setAction(Intent.ACTION_MAIN);
113 | i.setClass(this, SettingsActivity.class);
114 | startActivity(i);
115 | return true;
116 | case R.id.menu_auto_run:
117 | view.toggleAi();
118 | return true;
119 | case R.id.menu_new_game:
120 | view.stopAi();
121 | view.game.newGame();
122 | return true;
123 | }
124 | return true;
125 | }
126 |
127 | @Override
128 | public void onPause() {
129 | super.onPause();
130 |
131 | // If variety switched, do not save
132 | if (!save) return;
133 |
134 | SharedPreferences prefs = getSharedPreferences("state", 0);
135 | SharedPreferences.Editor edit = prefs.edit();
136 | Tile[][] field = view.game.grid.field;
137 | String[] saveState = new String[field[0].length];
138 | for (int xx = 0; xx < field.length; xx++) {
139 | saveState[xx] = "";
140 | for (int yy = 0; yy < field[0].length; yy++) {
141 | if (field[xx][yy] != null) {
142 | saveState[xx] += String.valueOf(field[xx][yy].getValue());
143 | } else {
144 | saveState[xx] += "0";
145 | }
146 | if (yy < field[0].length - 1) {
147 | saveState[xx] += "|";
148 | }
149 | }
150 | }
151 | for (int xx = 0; xx < saveState.length; xx++) {
152 | edit.putString("" + xx, saveState[xx]);
153 | }
154 | edit.putLong("score", view.game.score);
155 | edit.putLong("high score", view.game.highScore);
156 | edit.putBoolean("won", view.game.won);
157 | edit.putBoolean("lose", view.game.lose);
158 | edit.putInt("size", MainGame.numSquaresX);
159 | edit.apply();
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin or MSYS, switch paths to Windows format before running java
129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=`expr $i + 1`
158 | done
159 | case $i in
160 | 0) set -- ;;
161 | 1) set -- "$args0" ;;
162 | 2) set -- "$args0" "$args1" ;;
163 | 3) set -- "$args0" "$args1" "$args2" ;;
164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=`save "$@"`
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | exec "$JAVACMD" "$@"
184 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/MainGame.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import androidx.preference.PreferenceManager;
6 |
7 | import java.util.ArrayList;
8 | import java.util.Collections;
9 | import java.util.List;
10 |
11 | class MainGame {
12 |
13 | static final int SPAWN_ANIMATION = -1;
14 | static final int MOVE_ANIMATION = 0;
15 | static final int MERGE_ANIMATION = 1;
16 | static final int FADE_GLOBAL_ANIMATION = 0;
17 | private static final long MOVE_ANIMATION_TIME = MainView.BASE_ANIMATION_TIME;
18 | private static final long SPAWN_ANIMATION_TIME = (int) (MainView.BASE_ANIMATION_TIME * 1.5);
19 | private static final long NOTIFICATION_ANIMATION_TIME = MainView.BASE_ANIMATION_TIME * 5;
20 | private static final long NOTIFICATION_DELAY_TIME = MOVE_ANIMATION_TIME + SPAWN_ANIMATION_TIME;
21 | private static final String HIGH_SCORE = "high score";
22 | static int numSquaresX = 4;
23 | static int numSquaresY = 4;
24 | Grid grid;
25 | AnimationGrid aGrid;
26 | private boolean emulating = false;
27 | long score = 0;
28 | private long lastScore = 0;
29 | long highScore = 0;
30 | boolean won = false;
31 | boolean lose = false;
32 | private final Context mContext;
33 | private final MainView mView;
34 |
35 | MainGame(Context context, MainView view) {
36 | mContext = context;
37 | mView = view;
38 | }
39 |
40 | void newGame() {
41 | grid = new Grid(numSquaresX, numSquaresY);
42 | aGrid = new AnimationGrid(numSquaresX, numSquaresY);
43 | highScore = getHighScore();
44 | if (score >= highScore) {
45 | highScore = score;
46 | recordHighScore();
47 | }
48 | score = 0;
49 | won = false;
50 | lose = false;
51 | addStartTiles();
52 | mView.refreshLastTime = true;
53 | mView.reSyncTime();
54 | mView.postInvalidate();
55 | }
56 |
57 | private void addStartTiles() {
58 | int startTiles = 2;
59 | for (int xx = 0; xx < startTiles; xx++) {
60 | this.addRandomTile();
61 | }
62 | }
63 |
64 | private void addRandomTile() {
65 | if (grid.isCellsAvailable()) {
66 | addRandomTile(grid.randomAvailableCell());
67 | }
68 | }
69 |
70 | void addRandomTile(Cell cell) {
71 | int value = Math.random() < 0.1 ? 2 : 4;
72 | Tile tile = new Tile(cell, value);
73 | grid.insertTile(tile);
74 | if (!emulating) aGrid.startAnimation(tile.getX(), tile.getY(), SPAWN_ANIMATION,
75 | SPAWN_ANIMATION_TIME, MOVE_ANIMATION_TIME, null); //Direction: -1 = EXPANDING
76 | }
77 |
78 | private void recordHighScore() {
79 | SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mContext);
80 | SharedPreferences.Editor editor = settings.edit();
81 | editor.putLong(HIGH_SCORE, highScore);
82 | editor.apply();
83 | }
84 |
85 | private long getHighScore() {
86 | SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mContext);
87 | return settings.getLong(HIGH_SCORE, -1);
88 | }
89 |
90 | private void prepareTiles() {
91 | for (Tile[] array : grid.field) {
92 | for (Tile tile : array) {
93 | if (grid.isCellOccupied(tile)) {
94 | tile.setMergedFrom(null);
95 | //tile.savePosition();
96 | }
97 | }
98 | }
99 | }
100 |
101 | private void moveTile(Tile tile, Cell cell) {
102 | grid.field[tile.getX()][tile.getY()] = null;
103 | grid.field[cell.getX()][cell.getY()] = tile;
104 | tile.updatePosition(cell);
105 | }
106 |
107 | private void saveState() {
108 | grid.saveTiles();
109 | lastScore = score;
110 | }
111 |
112 | void revertState() {
113 | aGrid = new AnimationGrid(numSquaresX, numSquaresY);
114 | grid.revertTiles();
115 | score = lastScore;
116 |
117 | if (!emulating) {
118 | mView.refreshLastTime = true;
119 | mView.reSyncTime();
120 | mView.invalidate();
121 | }
122 | }
123 |
124 | boolean move(int direction) {
125 | saveState();
126 |
127 | if (!emulating) aGrid = new AnimationGrid(numSquaresX, numSquaresY);
128 | // 0: up, 1: right, 2: down, 3: left
129 | if (lose || won) {
130 | return false;
131 | }
132 | Cell vector = getVector(direction);
133 | List traversalsX = buildTraversalsX(vector);
134 | List traversalsY = buildTraversalsY(vector);
135 | boolean moved = false;
136 |
137 | prepareTiles();
138 |
139 | for (int xx : traversalsX) {
140 | for (int yy : traversalsY) {
141 | Cell cell = new Cell(xx, yy);
142 | Tile tile = grid.getCellContent(cell);
143 |
144 | if (tile != null) {
145 | Cell[] positions = findFarthestPosition(cell, vector);
146 | Tile next = grid.getCellContent(positions[1]);
147 |
148 | if (next != null && next.getValue() == tile.getValue() && next.getMergedFrom() == null) {
149 | Tile merged = new Tile(positions[1], tile.getValue() * 2);
150 | Tile[] temp = {tile, next};
151 | merged.setMergedFrom(temp);
152 |
153 | grid.insertTile(merged);
154 | grid.removeTile(tile);
155 |
156 | // Converge the two tiles' positions
157 | tile.updatePosition(positions[1]);
158 |
159 | if (!emulating) {
160 | int[] extras = {xx, yy};
161 | aGrid.startAnimation(merged.getX(), merged.getY(), MOVE_ANIMATION,
162 | MOVE_ANIMATION_TIME, 0, extras); //Direction: 0 = MOVING MERGED
163 | aGrid.startAnimation(merged.getX(), merged.getY(), MERGE_ANIMATION,
164 | SPAWN_ANIMATION_TIME, MOVE_ANIMATION_TIME, null);
165 | }
166 |
167 | // Update the score
168 | score = score + merged.getValue();
169 | highScore = Math.max(score, highScore);
170 |
171 | // The mighty max tile
172 | if (merged.getValue() == MainView.maxValue) {
173 | won = true;
174 | endGame();
175 | }
176 | } else {
177 | moveTile(tile, positions[0]);
178 | int[] extras = {xx, yy, 0};
179 | if (!emulating)
180 | aGrid.startAnimation(positions[0].getX(), positions[0].getY(), MOVE_ANIMATION, MOVE_ANIMATION_TIME, 0, extras); //Direction: 1 = MOVING NO MERGE
181 | }
182 |
183 | if (!positionsEqual(cell, tile)) {
184 | moved = true;
185 | }
186 | }
187 | }
188 | }
189 |
190 | if (moved) {
191 | if (!emulating && !MainView.inverseMode) {
192 | addRandomTile();
193 | }
194 |
195 | if (!movesAvailable()) {
196 | lose = true;
197 | endGame();
198 | }
199 |
200 | }
201 |
202 | if (!emulating) {
203 | mView.reSyncTime();
204 | mView.postInvalidate();
205 | }
206 |
207 | return moved;
208 | }
209 |
210 | private void endGame() {
211 | if (emulating) return;
212 |
213 | aGrid.startAnimation(-1, -1, FADE_GLOBAL_ANIMATION, NOTIFICATION_ANIMATION_TIME, NOTIFICATION_DELAY_TIME, null);
214 | if (score >= highScore) {
215 | highScore = score;
216 | recordHighScore();
217 | }
218 |
219 | grid.canRevert = false;
220 | }
221 |
222 | Cell getVector(int direction) {
223 | Cell[] map = {
224 | new Cell(0, -1), // up
225 | new Cell(1, 0), // right
226 | new Cell(0, 1), // down
227 | new Cell(-1, 0) // left
228 | };
229 | return map[direction];
230 | }
231 |
232 | private List buildTraversalsX(Cell vector) {
233 | List traversals = new ArrayList<>();
234 |
235 | for (int xx = 0; xx < numSquaresX; xx++) {
236 | traversals.add(xx);
237 | }
238 | if (vector.getX() == 1) {
239 | Collections.reverse(traversals);
240 | }
241 |
242 | return traversals;
243 | }
244 |
245 | private List buildTraversalsY(Cell vector) {
246 | List traversals = new ArrayList<>();
247 |
248 | for (int xx = 0; xx < numSquaresY; xx++) {
249 | traversals.add(xx);
250 | }
251 | if (vector.getY() == 1) {
252 | Collections.reverse(traversals);
253 | }
254 |
255 | return traversals;
256 | }
257 |
258 | Cell[] findFarthestPosition(Cell cell, Cell vector) {
259 | Cell previous;
260 | Cell nextCell = new Cell(cell.getX(), cell.getY());
261 | do {
262 | previous = nextCell;
263 | nextCell = new Cell(previous.getX() + vector.getX(),
264 | previous.getY() + vector.getY());
265 | } while (grid.isCellWithinBounds(nextCell) && grid.isCellAvailable(nextCell));
266 |
267 | return new Cell[]{previous, nextCell};
268 | }
269 |
270 | private boolean movesAvailable() {
271 | return grid.isCellsAvailable() || tileMatchesAvailable();
272 | }
273 |
274 | private boolean tileMatchesAvailable() {
275 | Tile tile;
276 |
277 | for (int xx = 0; xx < numSquaresX; xx++) {
278 | for (int yy = 0; yy < numSquaresY; yy++) {
279 | tile = grid.getCellContent(new Cell(xx, yy));
280 |
281 | if (tile != null) {
282 | for (int direction = 0; direction < 4; direction++) {
283 | Cell vector = getVector(direction);
284 | Cell cell = new Cell(xx + vector.getX(), yy + vector.getY());
285 |
286 | Tile other = grid.getCellContent(cell);
287 |
288 | if (other != null && other.getValue() == tile.getValue()) {
289 | return true;
290 | }
291 | }
292 | }
293 | }
294 | }
295 |
296 | return false;
297 | }
298 |
299 | private boolean positionsEqual(Cell first, Cell second) {
300 | return first.getX() == second.getX() && first.getY() == second.getY();
301 | }
302 |
303 | // Only for emulation
304 | @Override
305 | public MainGame clone() {
306 | MainGame newGame = new MainGame(mContext, null);
307 |
308 | newGame.grid = grid.clone();
309 | newGame.score = score;
310 | newGame.emulating = true;
311 |
312 | return newGame;
313 | }
314 | }
315 |
--------------------------------------------------------------------------------
/app/src/main/java/com/osfans/android2048/AI.java:
--------------------------------------------------------------------------------
1 | package com.osfans.android2048;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Date;
5 |
6 | /*
7 | *
8 | * This is a simple AI for the 2048 game
9 | * Based on alpha-beta method
10 | * Credits to: Matt Overlan
11 | *
12 | */
13 |
14 | class AI {
15 | private static final long MAX_CONSIDERING_TIME = 100;
16 | private static final float WEIGHT_SMOOTH = 0.1f, WEIGHT_MONO = 1.0f,
17 | WEIGHT_EMPTY = 2.7f, WEIGHT_MAX = 1.0f;
18 | //WEIGHT_ISLANDS = 0.5f, WEIGHT_TWOANDFOUR = 2.5f;
19 | private final MainGame mGame;
20 |
21 | AI(MainGame game) {
22 | mGame = game;
23 | }
24 |
25 | int getBestMove() {
26 |
27 | int bestMove = 0;
28 | int depth = 0;
29 | long start = new Date().getTime();
30 |
31 | do {
32 | int move = (Integer) search(mGame.clone(), depth, -10000, 10000, Player.DOCTOR)[0];
33 | if (move == -1) {
34 | break;
35 | } else {
36 | bestMove = move;
37 | depth++;
38 | }
39 | } while (new Date().getTime() - start < MAX_CONSIDERING_TIME);
40 |
41 | return bestMove;
42 | }
43 |
44 | /*
45 | *
46 | * Search for the best move
47 | * Based on alpha-beta search method
48 | * Simulates two players' game
49 | * The Doctor V.S. The Daleks
50 | *
51 | */
52 | private Object[] search(MainGame game, int depth, int alpha, int beta, Player player) {
53 | int bestMove = -1;
54 | int bestScore = 0;
55 |
56 | if (player == Player.DOCTOR) {
57 | // The Doctor's turn
58 | // Doctor wants to defeat the Daleks
59 | bestScore = alpha;
60 |
61 | for (int i = 0; i <= 3; i++) {
62 | MainGame g = game.clone();
63 |
64 | if (!g.move(i)) {
65 | continue;
66 | }
67 |
68 | if (game.won) {
69 | // If won, just do it
70 | return new Object[]{i, 10000};
71 | }
72 |
73 | int score;
74 |
75 | if (depth == 0) {
76 | // Just evaluate if this is at the bottom
77 | score = evaluate(g);
78 | } else {
79 | // Pass the game to the Daleks
80 | score = (Integer) search(g, depth - 1, bestScore, beta, Player.DALEKS)[1];
81 |
82 | // Don't search any further if won
83 | if (score > 9900) {
84 | score--;
85 | }
86 | }
87 |
88 | if (score > bestScore) {
89 | bestScore = score;
90 | bestMove = i;
91 | }
92 |
93 | // We have found a much much better move
94 | // So, cutoff
95 | if (bestScore > beta) {
96 | return new Object[]{bestMove, beta};
97 | }
98 | }
99 | } else if (player == Player.DALEKS) {
100 | // The Daleks' turn
101 | // "EXTERMINATE!"
102 | bestScore = beta;
103 |
104 | int maxScore = Integer.MIN_VALUE;
105 |
106 | ArrayList | | |