├── Android
└── Majong-AutoScore
│ ├── .gitignore
│ ├── .idea
│ ├── .name
│ ├── compiler.xml
│ ├── copyright
│ │ └── profiles_settings.xml
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── scopes
│ │ └── scope_settings.xml
│ └── vcs.xml
│ ├── Majong-AutoScore.iml
│ ├── app
│ ├── .gitignore
│ ├── app.iml
│ ├── build.gradle
│ ├── proguard-rules.txt
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── mahosyojyo
│ │ │ └── majong_autoscore
│ │ │ └── app
│ │ │ └── activities
│ │ │ └── GameSettingsActivity.java
│ │ └── res
│ │ ├── drawable-hdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-xhdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ ├── values
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ ├── strings_activity_game_settings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ └── pref_game_settings.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── settings.gradle
├── Design.md
├── JS
├── Majong.js
├── canvasjs-1.7.0
│ ├── canvasjs.min.js
│ ├── jquery.canvasjs.min.js
│ └── source
│ │ ├── canvasjs.js
│ │ ├── excanvas.js
│ │ └── jquery.canvasjs.js
├── chart
│ ├── Chart.js
│ ├── Chart.min.js
│ ├── chartline.css
│ ├── charttips.js
│ └── gulpfile.js
├── fanfuCal.js
├── img
│ ├── dice.png
│ ├── lichi.gif
│ ├── rcode.png
│ ├── saki.jpg
│ └── tab_bg1.gif
├── index.html
├── jquery-2.1.4.min.js
├── linechartDraw.js
├── piechartDraw.js
├── src
│ └── Chart.Line.js
├── style.css
├── support.js
├── switch.css
├── tab.js
└── tabstyle.css
├── LICENSE
├── README.md
└── iOS
└── src
├── .DS_Store
└── MahjongScoreCalculator
├── .DS_Store
├── MahjongScoreCalculator.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── fredfx.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── fredfx.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ ├── MahjongScoreCalculator.xcscheme
│ └── xcschememanagement.plist
├── MahjongScoreCalculator
├── .DS_Store
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-40.png
│ │ ├── Icon-40@2x.png
│ │ ├── Icon-40@3x.png
│ │ ├── Icon-60@2x.png
│ │ ├── Icon-60@3x.png
│ │ ├── Icon-76.png
│ │ ├── Icon-76@2x.png
│ │ ├── Icon-83.5@2x.png
│ │ ├── Icon-Small.png
│ │ ├── Icon-Small@2x.png
│ │ └── Icon-Small@3x.png
│ ├── Brand Assets.launchimage
│ │ └── Contents.json
│ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── GameCore
│ ├── MMSCGame.h
│ ├── MMSCGame.m
│ ├── MMSCGameManager.h
│ ├── MMSCGameManager.m
│ ├── MMSCPlayer.h
│ ├── MMSCPlayer.m
│ ├── MMSCRound.h
│ ├── MMSCRound.m
│ ├── MMSCUtil.h
│ ├── MMSCUtil.m
│ └── RoundResult
│ │ ├── MMSCDrawManganResult.h
│ │ ├── MMSCDrawManganResult.m
│ │ ├── MMSCDrawResult.h
│ │ ├── MMSCDrawResult.m
│ │ ├── MMSCNormalWinResult.h
│ │ ├── MMSCNormalWinResult.m
│ │ ├── MMSCRoundResult.h
│ │ ├── MMSCRoundResult.m
│ │ ├── MMSCTsuMoResult.h
│ │ ├── MMSCTsuMoResult.m
│ │ ├── MMSCTwoWinnerResult.h
│ │ └── MMSCTwoWinnerResult.m
├── Info.plist
├── View
│ ├── MMSCFontAndColorUtil.h
│ ├── MMSCFontAndColorUtil.m
│ ├── MMSCPlayerNameAndSeatController.h
│ ├── MMSCPlayerNameAndSeatController.m
│ ├── MMSCRandomSeatTipsController.h
│ ├── MMSCRandomSeatTipsController.m
│ ├── MMSCRichiController.h
│ ├── MMSCRichiController.m
│ ├── MMSCRoundResulltController.h
│ ├── MMSCRoundResulltController.m
│ ├── MMSCScoreTableCell.h
│ ├── MMSCScoreTableCell.m
│ ├── MMSCScoreTableController.h
│ └── MMSCScoreTableController.m
├── main.m
└── resource
│ ├── StartPage_ButtonBG@2x.png
│ ├── StartPage_ButtonBG_click@2x.png
│ ├── mahjong-appicon.png
│ ├── menu.png
│ ├── more_icon@2x.png
│ ├── more_icon_click@2x.png
│ ├── richiIcon-horizontal.png
│ └── richiIcon-vertical.png
└── MahjongScoreCalculatorTests
├── Info.plist
└── MahjongScoreCalculatorTests.m
/Android/Majong-AutoScore/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/.idea/.name:
--------------------------------------------------------------------------------
1 | Majong-AutoScore
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Android API 19 Platform
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/.idea/scopes/scope_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/Majong-AutoScore.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'android'
2 |
3 | android {
4 | compileSdkVersion 19
5 | buildToolsVersion '19.0.3'
6 |
7 | defaultConfig {
8 | minSdkVersion 11
9 | targetSdkVersion 19
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | runProguard false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(include: ['*.jar'], dir: 'libs')
23 | // compile 'com.android.support:support-v4:19.0.1'
24 | // compile 'com.android.support:appcompat-v7:19.0.1'
25 | // compile 'com.android.support:gridlayout-v7:19.0.1'
26 | }
27 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/proguard-rules.txt:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:/Program Files/AndroidStudio/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the ProGuard
5 | # include property in project.properties.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/java/com/mahosyojyo/majong_autoscore/app/activities/GameSettingsActivity.java:
--------------------------------------------------------------------------------
1 | package com.mahosyojyo.majong_autoscore.app.activities;
2 |
3 | import android.content.Context;
4 | import android.content.res.Configuration;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.preference.ListPreference;
8 | import android.preference.Preference;
9 | import android.preference.PreferenceActivity;
10 | import android.preference.PreferenceFragment;
11 | import android.preference.PreferenceManager;
12 |
13 |
14 | import com.mahosyojyo.majong_autoscore.app.R;
15 |
16 | /**
17 | * A {@link PreferenceActivity} that presents a set of application settings. On
18 | * handset devices, settings are presented as a single list. On tablets,
19 | * settings are split by category, with category headers shown to the left of
20 | * the list of settings.
21 | *
22 | * See
23 | * Android Design: Settings for design guidelines and the Settings
25 | * API Guide for more information on developing a Settings UI.
26 | */
27 | public class GameSettingsActivity extends PreferenceActivity {
28 | public static String SHARED_PREFERENCE_NAME;
29 |
30 | //TODO-添加设置界面图标,加入右上方设置完成按钮
31 | /**
32 | * Determines whether to always show the simplified settings UI, where
33 | * settings are presented in a single list. When false, settings are shown
34 | * as a master/detail two-pane view on tablets. When true, a single pane is
35 | * shown on tablets.
36 | */
37 | private static final boolean ALWAYS_SIMPLE_PREFS = false;
38 |
39 | @Override
40 | protected void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 |
43 | setupSimplePreferencesScreen();
44 |
45 | SHARED_PREFERENCE_NAME = getPreferenceManager().getSharedPreferencesName();
46 | }
47 |
48 | @Override
49 | protected boolean isValidFragment(String fragmentName) {
50 | return true;
51 | }
52 |
53 | /**
54 | * Shows the simplified settings UI if the device configuration if the
55 | * device configuration dictates that a simplified, single-pane UI should be
56 | * shown.
57 | */
58 | private void setupSimplePreferencesScreen() {
59 | if (!isSimplePreferences(this)) {
60 | return;
61 | }
62 |
63 | addPreferencesFromResource(R.xml.pref_game_settings);
64 |
65 | bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_gameName_key)));
66 | bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_playerNumber_key)));
67 | bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_enterWest_endCondition_key)));
68 | bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_enterWest_westEnd_key)));
69 |
70 | //配置游戏长度选项,只有选择半庄时才有西入选项
71 | String gameLengthKey = getString(R.string.pref_gameLength_key);
72 | Preference gameLengthPref = findPreference(gameLengthKey);
73 | gameLengthPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
74 | @Override
75 | public boolean onPreferenceChange(Preference preference, Object o) {
76 | return gameLengthChange(preference, o);
77 | }
78 | });
79 |
80 | gameLengthChange(gameLengthPref,
81 | PreferenceManager
82 | .getDefaultSharedPreferences(gameLengthPref.getContext())
83 | .getString(gameLengthKey, ""));
84 | }
85 |
86 | /*游戏长度选事件
87 | *除了基本的修改summary之外,添加对西入的控制
88 | * */
89 | private boolean gameLengthChange(Preference preference, Object o) {
90 | String value = o.toString();
91 | sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, o);
92 | findPreference(getString(R.string.pref_enterWestCategory_key)).setEnabled(value.equals("8"));
93 | return true;
94 | }
95 |
96 | /** {@inheritDoc} */
97 | @Override
98 | public boolean onIsMultiPane() {
99 | return isXLargeTablet(this) && !isSimplePreferences(this);
100 | }
101 |
102 | /**
103 | * Helper method to determine if the device has an extra-large screen. For
104 | * example, 10" tablets are extra-large.
105 | */
106 | private static boolean isXLargeTablet(Context context) {
107 | return (context.getResources().getConfiguration().screenLayout
108 | & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
109 | }
110 |
111 | /**
112 | * Determines whether the simplified settings UI should be shown. This is
113 | * true if this is forced via {@link #ALWAYS_SIMPLE_PREFS}, or the device
114 | * doesn't have newer APIs like {@link PreferenceFragment}, or the device
115 | * doesn't have an extra-large screen. In these cases, a single-pane
116 | * "simplified" settings UI should be shown.
117 | */
118 | private static boolean isSimplePreferences(Context context) {
119 | return ALWAYS_SIMPLE_PREFS
120 | || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB
121 | || !isXLargeTablet(context);
122 | }
123 |
124 | /**
125 | * A preference value change listener that updates the preference's summary
126 | * to reflect its new value.
127 | */
128 | private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
129 | @Override
130 | public boolean onPreferenceChange(Preference preference, Object value) {
131 | String stringValue = value.toString();
132 |
133 | if (preference instanceof ListPreference) {
134 | // For list preferences, look up the correct display value in
135 | // the preference's 'entries' list.
136 | ListPreference listPreference = (ListPreference) preference;
137 | int index = listPreference.findIndexOfValue(stringValue);
138 |
139 | // Set the summary to reflect the new value.
140 | preference.setSummary(
141 | index >= 0
142 | ? listPreference.getEntries()[index]
143 | : null);
144 | } else {
145 | // For all other preferences, set the summary to the value's
146 | // simple string representation.
147 | preference.setSummary(stringValue);
148 | }
149 | return true;
150 | }
151 | };
152 |
153 | /**
154 | * Binds a preference's summary to its value. More specifically, when the
155 | * preference's value is changed, its summary (line of text below the
156 | * preference title) is updated to reflect the value. The summary is also
157 | * immediately updated upon calling this method. The exact display format is
158 | * dependent on the type of preference.
159 | *
160 | * @see #sBindPreferenceSummaryToValueListener
161 | */
162 | private static void bindPreferenceSummaryToValue(Preference preference) {
163 | // Set the listener to watch for value changes.
164 | preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
165 |
166 | // Trigger the listener immediately with the preference's
167 | // current value.
168 | sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
169 | PreferenceManager
170 | .getDefaultSharedPreferences(preference.getContext())
171 | .getString(preference.getKey(), ""));
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/Android/Majong-AutoScore/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/Android/Majong-AutoScore/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/Android/Majong-AutoScore/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/Android/Majong-AutoScore/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Majong-AutoScore
5 | Hello world!
6 | Settings
7 | MainActivity
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/res/values/strings_activity_game_settings.xml:
--------------------------------------------------------------------------------
1 |
2 | Game设定
3 |
4 |
5 | game_name
6 | player_number
7 | game_length
8 | zero_continue
9 | enterWest_page
10 |
11 |
12 | Game信息
13 | 名称
14 | 今天的对♂日♂
15 | 人数
16 |
17 | - 四人
18 | - 三人
19 |
20 |
21 | - 4
22 | - 3
23 |
24 |
25 | 长度
26 |
27 | - 一局战
28 | - 东风战
29 | - 半庄战
30 | - 全庄战
31 | - 没完没了地战
32 |
33 |
34 | - 1
35 | - 4
36 | - 8
37 | - 16
38 | - -1
39 |
40 |
41 |
42 | 0分规则
43 |
44 | 0分继续
45 | 已经被婊到0分了,继续婊吗?
46 |
47 | 西入规则
48 |
49 | 无人超3W点西入
50 | 啊咧?不如一起战个痛
51 | enter_west
52 |
53 | 结束条件
54 | end_condition
55 |
56 | - 一方达到3W点立即结束
57 | - 当前一庄战结束
58 |
59 |
60 | - 1
61 | - 2
62 |
63 |
64 | west_end
65 | 西场结束无人超3W点判定
66 |
67 | - 平局
68 | - 战到底
69 | - 北入,北场结束仍无人3W点平局
70 |
71 |
72 | - 1
73 | - 2
74 | - 3
75 |
76 |
77 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/app/src/main/res/xml/pref_game_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
15 |
16 |
17 |
25 |
26 |
27 |
35 |
36 |
37 |
38 |
39 |
40 |
45 |
46 |
47 |
48 |
49 |
50 |
54 |
55 |
56 |
61 |
62 |
63 |
72 |
73 |
74 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | mavenCentral()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:0.9.+'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | mavenCentral()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/Android/Majong-AutoScore/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
7 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/Android/Majong-AutoScore/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/Design.md:
--------------------------------------------------------------------------------
1 | ###基本功能:
2 | * 自动计算庄家位置,本场数
3 | * 每一局需要输入谁自摸或者谁点谁的泡,或流听状况
4 | * 输入N番M符,自行计算所需点数分配(考虑庄家位置,本场数)
5 |
6 |
7 | =========
8 | JS版预计/已实现功能:
9 | * ~~可以设置一炮三响属于流局还是给点(默认流局)~~
10 | * (√2015-8-29)可以设置某方0分算结束还是继续(默认继续)
11 | * (√2015-8-29)可以设置玩家名称(默认东家起分别是Aさん,Bさん,Cさん,Dさん)
12 | * (√2015-8-29)可以设置模式是东风,半庄,全庄还是Endless(默认半庄),并且可以在结束后临时更改
13 | * (√2015-8-29)方桌界面,每一方显示自己的当前点数
14 | * (√2015-8-29)开关选择点差模式还是点数模式
15 | * (√2014–9-3)可以设置视角,(点差模式下)主视角方看到的是和别人之间的点差
16 | * (√2015–9-12)绘制比分趋势走向
17 | * 有列表查看比赛详情,包括胡牌状况,大小
18 | * (√2015–9-12)误操作允许撤销
19 | * ~~允许修改之前某一局的细节~~
20 | * (√2014–9-6)对于符数计算,可以通过点击选择暗刻数,明刻数,是否嵌章等情况自行计算
21 | * 设置计算顺位马
22 | * 累积多次对局成绩
23 | * ~~写cookies保留当前数据~~
24 | * (√2014-8-29)可以设置帮玩家随机抽位置(东南西北)
25 | * (√2015–9-13)增加辅助色子功能
26 | * 增加三麻模式
27 | * 大后期再研究团战模式
28 | * 可以任意设置其实比分(默认四麻25000,三麻35000,团战10000)
29 | * (√2015-8-30)半庄西入设置(默认西入)
30 | * (√2015–9-13)新手教程(copy from wiki)
31 | * ~~设置天风规则还是鸟笼规则~~
32 | * (√2015–92)需要额外增加九九流局和四风连打选项
33 |
34 |
35 |
36 |
37 |
38 |
39 | =========
40 | 安卓版预计/已实现功能:
41 | * 可以设置模式是东风,半庄,全庄还是Endless(默认半庄),并且可以在结束后临时更改
42 | * 可输入玩家名称(默认:天和、地和、人和、平和),点击确认后开始随机抽东南西北
43 | * 总览界面,界面显示当前场风、本场数以及每个玩家的自风,庄家高亮,显示从开局到当前的分数
44 | * 可以设置视角,主视角方看到的是和别人之间的点差
45 | * 可以设置4家皆不满3w点是否进入西风局(半庄)
46 | * 新手教程(链接到wiki)
47 |
48 | =========
49 | Yuki建议:
50 | * 可以设置帮玩家随机抽位置(东南西北)
51 |
--------------------------------------------------------------------------------
/JS/canvasjs-1.7.0/source/jquery.canvasjs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve CanvasJS jQuery Charting Plugin - http://canvasjs.com/
3 | * Copyright 2013 fenopix
4 | */
5 |
6 | /*
7 | * CanvasJS Charts follows Dual Licensing Model as mentioned below.
8 | *
9 | * ---------------------Free for Non-Commercial Use--------------------
10 | *
11 | * For non-commercial purposes you can use the software for free under Creative Commons Attribution-NonCommercial 3.0 License. Refer to the following link for further details on the same.
12 | * http://creativecommons.org/licenses/by-nc/3.0/deed.en_US
13 | *
14 | * ---------------------Commercial License--------------------
15 | * Commercial use of CanvasJS requires you to purchase a license. Without a commercial license you can use it for evaluation purposes only. Please refer to the following link for further details.
16 | * http://canvasjs.com/
17 | *
18 | */
19 |
20 | (function ($, window, document, undefined) {
21 |
22 | $.fn.CanvasJSChart = function (options) {
23 |
24 | if (options) {
25 |
26 | var $el = this.first();
27 | var container = this[0];
28 | var chart = new CanvasJS.Chart(container, options);
29 |
30 | $el.children(".canvasjs-chart-container").data("canvasjsChartRef", chart);
31 |
32 | chart.render();
33 |
34 | return this;
35 |
36 | } else {
37 |
38 | return this.first().children(".canvasjs-chart-container").data("canvasjsChartRef");
39 |
40 | }
41 | }
42 |
43 | }(jQuery, window, document));
--------------------------------------------------------------------------------
/JS/chart/chartline.css:
--------------------------------------------------------------------------------
1 | #chartjs-tooltip {
2 | opacity: 1;
3 | position: absolute;
4 | background: rgba(0, 0, 0, .7);
5 | color: white;
6 | padding: 3px;
7 | border-radius: 3px;
8 | -webkit-transition: all .1s ease;
9 | transition: all .1s ease;
10 | pointer-events: none;
11 | -webkit-transform: translate(-50%, 0);
12 | transform: translate(-50%, 0);
13 | }
14 |
15 | .chartjs-tooltip-key {
16 | display: inline-block;
17 | width: 10px;
18 | height: 10px;
19 | }
20 |
21 | #myChart {
22 | float: left;
23 | margin-left: 30px;
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/JS/chart/charttips.js:
--------------------------------------------------------------------------------
1 | function set_tooltip() {
2 | Chart.defaults.global.pointHitDetectionRadius = 1;
3 | Chart.defaults.global.customTooltips = function (tooltip) {
4 | var tooltipEl = $('#chartjs-tooltip');
5 |
6 | if (!tooltip) {
7 | tooltipEl.css({
8 | opacity: 0
9 | });
10 | return;
11 | }
12 |
13 | tooltipEl.removeClass('above below');
14 | tooltipEl.addClass(tooltip.yAlign);
15 |
16 | var innerHtml = tooltip.title;
17 | for (var i = tooltip.labels.length - 1; i >= 0; i--) {
18 | innerHtml += [
19 | '
',
20 | '' + player[i].playerName,
21 | ' ' + tooltip.labels[i] + '',
22 | '
'
23 | ].join('');
24 | }
25 | tooltipEl.html(innerHtml);
26 |
27 | tooltipEl.css({
28 | opacity: 1,
29 | left: tooltip.chart.canvas.offsetLeft + tooltip.x + 'px',
30 | top: tooltip.chart.canvas.offsetTop + tooltip.y + 'px',
31 | fontFamily: tooltip.fontFamily,
32 | fontSize: tooltip.fontSize,
33 | fontStyle: tooltip.fontStyle,
34 | });
35 | };
36 | }
--------------------------------------------------------------------------------
/JS/chart/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp'),
2 | concat = require('gulp-concat'),
3 | uglify = require('gulp-uglify'),
4 | util = require('gulp-util'),
5 | jshint = require('gulp-jshint'),
6 | size = require('gulp-size'),
7 | connect = require('gulp-connect'),
8 | replace = require('gulp-replace'),
9 | htmlv = require('gulp-html-validator'),
10 | inquirer = require('inquirer'),
11 | semver = require('semver'),
12 | exec = require('child_process').exec,
13 | fs = require('fs'),
14 | package = require('./package.json'),
15 | bower = require('./bower.json');
16 |
17 | var srcDir = './src/';
18 | /*
19 | * Usage : gulp build --types=Bar,Line,Doughnut
20 | * Output: - A built Chart.js file with Core and types Bar, Line and Doughnut concatenated together
21 | * - A minified version of this code, in Chart.min.js
22 | */
23 |
24 | gulp.task('build', function(){
25 |
26 | // Default to all of the chart types, with Chart.Core first
27 | var srcFiles = [FileName('Core')],
28 | isCustom = !!(util.env.types),
29 | outputDir = (isCustom) ? 'custom' : '.';
30 | if (isCustom){
31 | util.env.types.split(',').forEach(function(type){ return srcFiles.push(FileName(type));});
32 | }
33 | else{
34 | // Seems gulp-concat remove duplicates - nice!
35 | // So we can use this to sort out dependency order - aka include Core first!
36 | srcFiles.push(srcDir+'*');
37 | }
38 |
39 | return gulp.src(srcFiles)
40 | .pipe(concat('Chart.js'))
41 | .pipe(replace('{{ version }}', package.version))
42 | .pipe(gulp.dest(outputDir))
43 | .pipe(uglify({preserveComments:'some'}))
44 | .pipe(concat('Chart.min.js'))
45 | .pipe(gulp.dest(outputDir));
46 |
47 | function FileName(moduleName){
48 | return srcDir+'Chart.'+moduleName+'.js';
49 | }
50 | });
51 |
52 | /*
53 | * Usage : gulp bump
54 | * Prompts: Version increment to bump
55 | * Output: - New version number written into package.json & bower.json
56 | */
57 |
58 | gulp.task('bump', function(complete){
59 | util.log('Current version:', util.colors.cyan(package.version));
60 | var choices = ['major', 'premajor', 'minor', 'preminor', 'patch', 'prepatch', 'prerelease'].map(function(versionType){
61 | return versionType + ' (v' + semver.inc(package.version, versionType) + ')';
62 | });
63 | inquirer.prompt({
64 | type: 'list',
65 | name: 'version',
66 | message: 'What version update would you like?',
67 | choices: choices
68 | }, function(res){
69 | var increment = res.version.split(' ')[0],
70 | newVersion = semver.inc(package.version, increment);
71 |
72 | // Set the new versions into the bower/package object
73 | package.version = newVersion;
74 | bower.version = newVersion;
75 |
76 | // Write these to their own files, then build the output
77 | fs.writeFileSync('package.json', JSON.stringify(package, null, 2));
78 | fs.writeFileSync('bower.json', JSON.stringify(bower, null, 2));
79 |
80 | complete();
81 | });
82 | });
83 |
84 | gulp.task('release', ['build'], function(){
85 | exec('git tag -a v' + package.version);
86 | });
87 |
88 | gulp.task('jshint', function(){
89 | return gulp.src(srcDir + '*.js')
90 | .pipe(jshint())
91 | .pipe(jshint.reporter('default'));
92 | });
93 |
94 | gulp.task('valid', function(){
95 | return gulp.src('samples/*.html')
96 | .pipe(htmlv());
97 | });
98 |
99 | gulp.task('library-size', function(){
100 | return gulp.src('Chart.min.js')
101 | .pipe(size({
102 | gzip: true
103 | }));
104 | });
105 |
106 | gulp.task('module-sizes', function(){
107 | return gulp.src(srcDir + '*.js')
108 | .pipe(uglify({preserveComments:'some'}))
109 | .pipe(size({
110 | showFiles: true,
111 | gzip: true
112 | }));
113 | });
114 |
115 | gulp.task('watch', function(){
116 | gulp.watch('./src/*', ['build']);
117 | });
118 |
119 | gulp.task('test', ['jshint', 'valid']);
120 |
121 | gulp.task('size', ['library-size', 'module-sizes']);
122 |
123 | gulp.task('default', ['build', 'watch']);
124 |
125 | gulp.task('server', function(){
126 | connect.server({
127 | port: 8000
128 | });
129 | });
130 |
131 | // Convenience task for opening the project straight from the command line
132 | gulp.task('_open', function(){
133 | exec('open http://localhost:8000');
134 | exec('subl .');
135 | });
136 |
137 | gulp.task('dev', ['server', 'default']);
138 |
--------------------------------------------------------------------------------
/JS/fanfuCal.js:
--------------------------------------------------------------------------------
1 | function fanshu_click(idx) {
2 | var cur_btn = $q('#fanshu_field .fanfu_btn', idx);
3 | if (cur_btn.hasClass('fanshu_clk')) {
4 | if (idx == 8) { //役满递增处理
5 | var cur_yiman = parseInt(cur_btn[0].value[0]);
6 | if (cur_yiman < 6) {
7 | cur_btn[0].value = (cur_yiman + 1) + "倍役满";
8 | } else {
9 | cur_btn[0].value = "1倍役满";
10 | cur_btn.removeClass('fanshu_clk');
11 | ClearFushuSetting(false);
12 | }
13 | } else {
14 | cur_btn.removeClass('fanshu_clk');
15 | ClearFushuSetting(false);
16 | }
17 | } else {
18 | cur_btn.addClass('fanshu_clk');
19 | $('#fanshu_field .fanfu_btn').not(function (id) {
20 | return id == idx
21 | }).removeClass('fanshu_clk');
22 | if (idx >= 4) {
23 | ClearFushuSetting(true);
24 | } else {
25 | ClearFushuSetting(false);
26 | }
27 | }
28 | }
29 |
30 | function ClearFushuSetting(IsClear) {
31 | $('#fushu_field .fanfu_btn').attr('disabled', IsClear);
32 | $('.fanfu_help_btn').attr('disabled', IsClear);
33 | $('tr.mianzi_tr td .fu_btn').attr('disabled', IsClear);
34 | $('#fushu_ok').attr('disabled', IsClear);
35 |
36 | if (IsClear) {
37 | $('#fushu_field .fanfu_btn').removeClass('fushu_clk');
38 | $('.fanfu_help_btn').removeClass('fanfu_help_clk');
39 | $('tr.mianzi_tr td .fu_btn').removeClass('mz_cal_clk');
40 | $('tr.mianzi_tr td .fu_btn').each(function (idx) {
41 | $q('tr.mianzi_tr td .fu_btn', idx)[0].value = 0;
42 | })
43 | }
44 | }
45 |
46 |
47 | function fushu_click(idx) {
48 | var cur_btn = $q('#fushu_field .fanfu_btn', idx);
49 | if (cur_btn.hasClass('fushu_clk')) {
50 | cur_btn.removeClass('fushu_clk');
51 | } else {
52 | cur_btn.addClass('fushu_clk');
53 | $('#fushu_field .fanfu_btn').not(function (id) {
54 | return id == idx
55 | }).removeClass('fushu_clk');
56 | }
57 | }
58 |
59 | function toolhelp_clk(idx) {
60 | var cur_btn = $q('.fanfu_help_btn', idx);
61 | if (cur_btn.hasClass('fanfu_help_clk')) {
62 | cur_btn.removeClass('fanfu_help_clk');
63 | } else {
64 | cur_btn.addClass('fanfu_help_clk');
65 | }
66 | SetFushu();
67 | }
68 |
69 |
70 | function mianzi_cal(idx1, idx2) {
71 | var validLeft = 4;
72 | $('tr.mianzi_tr td .fu_btn').each(function (i) {
73 | validLeft -= parseInt($q('tr.mianzi_tr td .fu_btn', i)[0].value);
74 | });
75 | var idx = idx1 * 2 + idx2;
76 | var cur_btn = $q('tr.mianzi_tr td .fu_btn', idx);
77 |
78 | var cur_val = parseInt(cur_btn[0].value);
79 | cur_val = (validLeft == 0) ? 0 : cur_val + 1;
80 | cur_btn[0].value = '' + cur_val;
81 | if (cur_btn[0].value > 0)
82 | cur_btn.addClass('mz_cal_clk');
83 | else
84 | cur_btn.removeClass('mz_cal_clk');
85 |
86 | SetFushu();
87 | }
88 |
89 | function CalFu() { //计算符数
90 | var fushu = 20;
91 | if ($q('.fanfu_help_btn', 0).hasClass('fanfu_help_clk'))
92 | fushu += 2; //听牌符数
93 | if ($q('.fanfu_help_btn', 1).hasClass('fanfu_help_clk'))
94 | fushu += 10; //听牌符数
95 | if ($q('.fanfu_help_btn', 2).hasClass('fanfu_help_clk'))
96 | fushu += 2; //听牌符数
97 |
98 | for (var idx1 = 0; idx1 < 4; idx1++) {
99 | for (var idx2 = 0; idx2 < 2; idx2++) {
100 | var t = Math.pow(2, idx1 + idx2 + 1);
101 | var idx = idx1 * 2 + idx2;
102 | var cur_btn = $q('tr.mianzi_tr td .fu_btn', idx);
103 | fushu += t * parseInt(cur_btn[0].value);
104 | }
105 | }
106 | return fushu;
107 | }
108 |
109 | function SetFushu() {
110 | var fu = CalFu();
111 | var result_f = fu - fu % 10 + (fu % 10 == 0 ? 0 : 10);
112 | if (result_f > 120)
113 | result_f = 120; ///////////////最好考证一下!!!
114 | $('#fushu_field .fanfu_btn').each(function (idx) {
115 | $q('#fushu_field .fanfu_btn', idx)[0].value == result_f ? $q('#fushu_field .fanfu_btn', idx).addClass('fushu_clk') : $q('#fushu_field .fanfu_btn', idx).removeClass('fushu_clk');
116 | });
117 | }
118 |
119 | function liuju_clk(idx) {
120 | if ($q('.liuju_icon', idx).hasClass('liuju_noting')) {
121 | $q('.liuju_icon', idx).removeClass('liuju_noting');
122 | $q('.liuju_icon', idx).addClass('liuju_ting');
123 | } else {
124 | $q('.liuju_icon', idx).removeClass('liuju_ting');
125 | $q('.liuju_icon', idx).addClass('liuju_noting');
126 | }
127 | }
128 |
129 | function ScoreUpper(score) {
130 | return parseInt(score / 100) * 100 + (score % 100 == 0 ? 0 : 100);
131 | }
--------------------------------------------------------------------------------
/JS/img/dice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/JS/img/dice.png
--------------------------------------------------------------------------------
/JS/img/lichi.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/JS/img/lichi.gif
--------------------------------------------------------------------------------
/JS/img/rcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/JS/img/rcode.png
--------------------------------------------------------------------------------
/JS/img/saki.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/JS/img/saki.jpg
--------------------------------------------------------------------------------
/JS/img/tab_bg1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/JS/img/tab_bg1.gif
--------------------------------------------------------------------------------
/JS/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 日麻算分器 Version 2.0
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | - 设置
29 | - 统计
30 | - 帮助
31 | - 关于
32 |
33 |
34 |
35 |
36 |
65 |
66 |
67 |
68 |
还没想好这里要怎么弄啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊
69 |
70 |

71 |
72 |
73 |
74 |
一番:
75 |
立直,一发,门清自摸,断幺,平和,一杯口,役牌,宝牌,领上,抢杠,海底,河底
76 |
77 |
两番:
78 |
79 |
三色*
,
80 |
一气*
,对对,三暗刻,七对,
81 |
混全*
,混老头,三色同刻,三杠子,小三元,W立
82 |
83 |
84 |
三番:
85 |
86 |
混一色*
,
87 |
纯全带幺*
,二杯口
88 |
89 |
满贯以上:
90 |
92 |
93 |
役满:
94 |
大三元,
95 |
四暗刻*
,
96 |
国士*
,字一色,
97 |
小四喜*
,绿一色,天地人胡,
98 |
九莲*
,四杠子,累计,大竹轮,大数临,大车轮,大七星
99 |
100 |
101 |
MORE:
日麻役详细,
102 |
日麻规则,
算分表
103 |
104 |
105 | Version: 2.0
106 |
版权归
107 |
Vespa 所有
108 |
项目主页:
109 |
https://github.com/Mahosyojyo
110 |
反馈:
111 |
点击提BUG
112 |
113 |

114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
129 |
130 |
131 | 场况:
132 |
133 |
本场数:
134 |
135 |

×
136 |
0
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
0
147 |
148 |
149 |
位
150 |
151 |
152 |
155 |
156 |
157 |
158 |
159 |
160 |
0
161 |
162 |
163 |
位
164 |
165 |
166 |
169 |
170 |
171 |
172 |
173 |
174 |
0
175 |
176 |
177 |
位
178 |
179 |
180 |
183 |
184 |
185 |
186 |
187 |
188 |
0
189 |
190 |
191 |
位
192 |
193 |
194 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
285 |
286 |
287 |
288 |
289 |
--------------------------------------------------------------------------------
/JS/linechartDraw.js:
--------------------------------------------------------------------------------
1 | function DrawLine() {
2 | // var data = {
3 | // labels: [],
4 | // datasets: [
5 | // {
6 | // fillColor: "rgba(255,255,255,0)",
7 | // strokeColor: "rgb(220,0,220)", //线
8 | // pointColor: "rgb(203, 22, 29)", //点
9 | // pointStrokeColor: "#f00", //圈
10 | // data: []
11 | // },
12 | // {
13 | // fillColor: "rgba(255,255,255,0)",
14 | // strokeColor: "rgb(18, 235, 60)",
15 | // pointColor: "rgb(0, 250, 135)",
16 | // pointStrokeColor: "#21fa7d",
17 | // data: []
18 | // }
19 | // ,
20 | // {
21 | // fillColor: "rgba(255,255,255,0)",
22 | // strokeColor: "rgb(25, 41, 234)",
23 | // pointColor: "rgb(14, 148, 198)",
24 | // pointStrokeColor: "#4e60c1",
25 | // data: []
26 | // }
27 | // ,
28 | // {
29 | // fillColor: "rgba(255,255,255,0)",
30 | // strokeColor: "rgb(237, 128, 109)",
31 | // pointColor: "rgb(226, 93, 34)",
32 | // pointStrokeColor: "#c9861c",
33 | // data: []
34 | // }
35 | // ]
36 | // };
37 | // for (var i = 0; i < game_state.length; i++) {
38 | // data['labels'].push(game_state[i].game.changfeng + game_state[i].game.jushu + '-' + game_state[i].game.benchang);
39 | // for (var player_idx = 0; player_idx < 4; player_idx++) {
40 | // data['datasets'][player_idx]['data'].push(game_state[i].player[player_idx].Point);
41 | // }
42 | // }
43 | // var ctx = document.getElementById("myChart").getContext("2d");
44 | // window.myLine = new Chart(ctx).Line(data, {
45 | // scaleOverlay: true,
46 | // showScale: true,
47 | // scaleLineColor: "rgba(0,0,0,1)",
48 | // scaleLineWidth: 1,
49 | // scaleShowLabels: true,
50 | // scaleLabel: "<%=value%>",
51 | // scaleFontFamily: "'Arial'",
52 | // scaleFontSize: 20,
53 | // scaleFontStyle: "normal",
54 | // scaleFontColor: "#666",
55 | // scaleShowGridLines: true,
56 | // scaleGridLineColor: "rgba(0,0,0,.1)",
57 | // scaleGridLineWidth: 3,
58 | // bezierCurve: Draw_Line_Curl,
59 | // pointDot: true,
60 | // pointDotRadius: 5,
61 | // pointDotStrokeWidth: 1,
62 | // datasetStrokeWidth: 2,
63 | // datasetFill: false,
64 | // });
65 |
66 | var chart = new CanvasJS.Chart("linechartContainer", {
67 | animationEnabled: true,
68 | animationDuration: 500,
69 | title: {
70 | //text: "分数走势图"
71 | },
72 | axisY: {
73 | valueFormatString: "#######",
74 | labelFontSize: 20,
75 | gridDashType: "dot",
76 | gridThickness: 2,
77 | minimum: 0,
78 | stripLines: [
79 | {
80 | startValue: 25000,
81 | endValue: 100000,
82 | color: "#f0ddba"
83 | },
84 | {
85 | startValue: 0,
86 | endValue: 25000,
87 | color: "#e0d6d6"
88 | }
89 | ]
90 | },
91 | axisX: {
92 | labelAngle: -45,
93 | labelFontSize: 20,
94 | minimum: 0,
95 | gridDashType: "dot",
96 | gridThickness: 2,
97 | gridColor: "orange",
98 | },
99 | legend: {
100 | fontSize: 18,
101 | horizontalAlign: "right",
102 | verticalAlign: "top",
103 | },
104 | toolTip: {
105 | shared: true,
106 | contentFormatter: function (e) {
107 | var content = "" + e.entries[0].dataPoint.label + "";
108 | for (var i = 0; i < e.entries.length; i++) {
109 | content += "" + e.entries[i].dataSeries.name + ":" + "" + e.entries[i].dataPoint.y + "";
110 | content += "
";
111 | }
112 | return content;
113 | }
114 | },
115 | data: [
116 | {
117 | name: player[0].playerName,
118 | showInLegend: true,
119 | highlightEnabled: true,
120 | type: Draw_Line_Curl ? 'spline' : 'line',
121 | dataPoints: []
122 | },
123 | {
124 | name: player[1].playerName,
125 | showInLegend: true,
126 | highlightEnabled: true,
127 | type: Draw_Line_Curl ? 'spline' : 'line',
128 | dataPoints: []
129 | },
130 | {
131 | name: player[2].playerName,
132 | showInLegend: true,
133 | highlightEnabled: true,
134 | type: Draw_Line_Curl ? 'spline' : 'line',
135 | dataPoints: []
136 | }, {
137 | name: player[3].playerName,
138 | showInLegend: true,
139 | highlightEnabled: true,
140 | type: Draw_Line_Curl ? 'spline' : 'line',
141 | dataPoints: []
142 | }
143 | ]
144 |
145 | });
146 |
147 | var last_score = [InitScore, InitScore, InitScore, InitScore];
148 | var min_score = 100000;
149 | for (var i = 0; i < game_state.length; i++) {
150 | for (var player_idx = 0; player_idx < 4; player_idx++) {
151 | chart.options.data[player_idx].dataPoints.push({
152 | y: game_state[i].player[player_idx].Point,
153 | label: game_state[i].game.changfeng + game_state[i].game.jushu + '-' + game_state[i].game.benchang,
154 | markerSize: 12,
155 | markerColor: game_state[i].player[player_idx].Point > last_score[player_idx] ? "#2defd8" : (game_state[i].player[player_idx].Point < last_score[player_idx] ? "tomato" : "green"),
156 | markerType: game_state[i].player[player_idx].Point > last_score[player_idx] ? "triangle" : (game_state[i].player[player_idx].Point < last_score[player_idx] ? "cross" : "square")
157 | })
158 | last_score[player_idx] = game_state[i].player[player_idx].Point;
159 | if (game_state[i].player[player_idx].Point < min_score)
160 | min_score = game_state[i].player[player_idx].Point;
161 | }
162 | }
163 | chart.options.axisY.minimum = min_score - 1000;
164 | chart.render();
165 | }
--------------------------------------------------------------------------------
/JS/piechartDraw.js:
--------------------------------------------------------------------------------
1 | function FigureStartAngel(maxidx) {
2 | var player1Rate = player[maxidx].Point / (player[0].Point + player[1].Point + player[2].Point + player[3].Point);
3 | return ((5-maxidx)%4)*90-player1Rate/2*360;
4 | }
5 |
6 | function DrawPieChart() {
7 | var maxidx = 0;
8 | for (var i = 1; i < 4; i++) {
9 | if (player[i].Point > player[maxidx].Point)
10 | maxidx = i;
11 | }
12 | var chart = new CanvasJS.Chart("chartContainer", {
13 | theme: "theme2",
14 | animationEnabled: true,
15 | animationDuration: 500,
16 | title: {
17 | text: ""
18 | },
19 | data: [
20 | {
21 | type: "pie",
22 | showInLegend: true,
23 | toolTipContent: "{indexLabel}
{y}",
24 | indexLabelPlacement: "inside",
25 | indexLabelFontColor: "black",
26 | indexLabelFontSize: 18,
27 | showInLegend: false,
28 | startAngle: FigureStartAngel(maxidx),
29 | dataPoints: [
30 | {
31 | y: player[maxidx].Point,
32 | indexLabel: player[maxidx].playerName
33 | },
34 | {
35 | y: player[(maxidx+3)%4].Point,
36 | indexLabel: player[(maxidx+3)%4].playerName
37 | },
38 | {
39 | y: player[(maxidx+2)%4].Point,
40 | indexLabel: player[(maxidx+2)%4].playerName
41 | },
42 | {
43 | y: player[(maxidx+1)%4].Point,
44 | indexLabel: player[(maxidx+1)%4].playerName
45 | }
46 | ]
47 | }
48 | ]
49 | });
50 | chart.render();
51 | }
--------------------------------------------------------------------------------
/JS/src/Chart.Line.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | "use strict";
3 |
4 | var root = this,
5 | Chart = root.Chart,
6 | helpers = Chart.helpers;
7 |
8 | var defaultConfig = {
9 |
10 | ///Boolean - Whether grid lines are shown across the chart
11 | scaleShowGridLines : true,
12 |
13 | //String - Colour of the grid lines
14 | scaleGridLineColor : "rgba(0,0,0,.05)",
15 |
16 | //Number - Width of the grid lines
17 | scaleGridLineWidth : 1,
18 |
19 | //Boolean - Whether to show horizontal lines (except X axis)
20 | scaleShowHorizontalLines: true,
21 |
22 | //Boolean - Whether to show vertical lines (except Y axis)
23 | scaleShowVerticalLines: true,
24 |
25 | //Boolean - Whether the line is curved between points
26 | bezierCurve : true,
27 |
28 | //Number - Tension of the bezier curve between points
29 | bezierCurveTension : 0.4,
30 |
31 | //Boolean - Whether to show a dot for each point
32 | pointDot : true,
33 |
34 | //Number - Radius of each point dot in pixels
35 | pointDotRadius : 4,
36 |
37 | //Number - Pixel width of point dot stroke
38 | pointDotStrokeWidth : 1,
39 |
40 | //Number - amount extra to add to the radius to cater for hit detection outside the drawn point
41 | pointHitDetectionRadius : 20,
42 |
43 | //Boolean - Whether to show a stroke for datasets
44 | datasetStroke : true,
45 |
46 | //Number - Pixel width of dataset stroke
47 | datasetStrokeWidth : 2,
48 |
49 | //Boolean - Whether to fill the dataset with a colour
50 | datasetFill : true,
51 |
52 | //String - A legend template
53 | legendTemplate : "-legend\"><% for (var i=0; i- \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
<%}%>
",
54 |
55 | //Boolean - Whether to horizontally center the label and point dot inside the grid
56 | offsetGridLines : false
57 |
58 | };
59 |
60 |
61 | Chart.Type.extend({
62 | name: "Line",
63 | defaults : defaultConfig,
64 | initialize: function(data){
65 | //Declare the extension of the default point, to cater for the options passed in to the constructor
66 | this.PointClass = Chart.Point.extend({
67 | offsetGridLines : this.options.offsetGridLines,
68 | strokeWidth : this.options.pointDotStrokeWidth,
69 | radius : this.options.pointDotRadius,
70 | display: this.options.pointDot,
71 | hitDetectionRadius : this.options.pointHitDetectionRadius,
72 | ctx : this.chart.ctx,
73 | inRange : function(mouseX){
74 | return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2));
75 | }
76 | });
77 |
78 | this.datasets = [];
79 |
80 | //Set up tooltip events on the chart
81 | if (this.options.showTooltips){
82 | helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
83 | var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
84 | this.eachPoints(function(point){
85 | point.restore(['fillColor', 'strokeColor']);
86 | });
87 | helpers.each(activePoints, function(activePoint){
88 | activePoint.fillColor = activePoint.highlightFill;
89 | activePoint.strokeColor = activePoint.highlightStroke;
90 | });
91 | this.showTooltip(activePoints);
92 | });
93 | }
94 |
95 | //Iterate through each of the datasets, and build this into a property of the chart
96 | helpers.each(data.datasets,function(dataset){
97 |
98 | var datasetObject = {
99 | label : dataset.label || null,
100 | fillColor : dataset.fillColor,
101 | strokeColor : dataset.strokeColor,
102 | pointColor : dataset.pointColor,
103 | pointStrokeColor : dataset.pointStrokeColor,
104 | points : []
105 | };
106 |
107 | this.datasets.push(datasetObject);
108 |
109 |
110 | helpers.each(dataset.data,function(dataPoint,index){
111 | //Add a new point for each piece of data, passing any required data to draw.
112 | datasetObject.points.push(new this.PointClass({
113 | value : dataPoint,
114 | label : data.labels[index],
115 | datasetLabel: dataset.label,
116 | strokeColor : dataset.pointStrokeColor,
117 | fillColor : dataset.pointColor,
118 | highlightFill : dataset.pointHighlightFill || dataset.pointColor,
119 | highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
120 | }));
121 | },this);
122 |
123 | this.buildScale(data.labels);
124 |
125 |
126 | this.eachPoints(function(point, index){
127 | helpers.extend(point, {
128 | x: this.scale.calculateX(index),
129 | y: this.scale.endPoint
130 | });
131 | point.save();
132 | }, this);
133 |
134 | },this);
135 |
136 |
137 | this.render();
138 | },
139 | update : function(){
140 | this.scale.update();
141 | // Reset any highlight colours before updating.
142 | helpers.each(this.activeElements, function(activeElement){
143 | activeElement.restore(['fillColor', 'strokeColor']);
144 | });
145 | this.eachPoints(function(point){
146 | point.save();
147 | });
148 | this.render();
149 | },
150 | eachPoints : function(callback){
151 | helpers.each(this.datasets,function(dataset){
152 | helpers.each(dataset.points,callback,this);
153 | },this);
154 | },
155 | getPointsAtEvent : function(e){
156 | var pointsArray = [],
157 | eventPosition = helpers.getRelativePosition(e);
158 | helpers.each(this.datasets,function(dataset){
159 | helpers.each(dataset.points,function(point){
160 | if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point);
161 | });
162 | },this);
163 | return pointsArray;
164 | },
165 | buildScale : function(labels){
166 | var self = this;
167 |
168 | var dataTotal = function(){
169 | var values = [];
170 | self.eachPoints(function(point){
171 | values.push(point.value);
172 | });
173 |
174 | return values;
175 | };
176 |
177 | var scaleOptions = {
178 | templateString : this.options.scaleLabel,
179 | height : this.chart.height,
180 | width : this.chart.width,
181 | ctx : this.chart.ctx,
182 | textColor : this.options.scaleFontColor,
183 | offsetGridLines : this.options.offsetGridLines,
184 | fontSize : this.options.scaleFontSize,
185 | fontStyle : this.options.scaleFontStyle,
186 | fontFamily : this.options.scaleFontFamily,
187 | valuesCount : labels.length,
188 | beginAtZero : this.options.scaleBeginAtZero,
189 | integersOnly : this.options.scaleIntegersOnly,
190 | calculateYRange : function(currentHeight){
191 | var updatedRanges = helpers.calculateScaleRange(
192 | dataTotal(),
193 | currentHeight,
194 | this.fontSize,
195 | this.beginAtZero,
196 | this.integersOnly
197 | );
198 | helpers.extend(this, updatedRanges);
199 | },
200 | xLabels : labels,
201 | font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
202 | lineWidth : this.options.scaleLineWidth,
203 | lineColor : this.options.scaleLineColor,
204 | showHorizontalLines : this.options.scaleShowHorizontalLines,
205 | showVerticalLines : this.options.scaleShowVerticalLines,
206 | gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
207 | gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
208 | padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,
209 | showLabels : this.options.scaleShowLabels,
210 | display : this.options.showScale
211 | };
212 |
213 | if (this.options.scaleOverride){
214 | helpers.extend(scaleOptions, {
215 | calculateYRange: helpers.noop,
216 | steps: this.options.scaleSteps,
217 | stepValue: this.options.scaleStepWidth,
218 | min: this.options.scaleStartValue,
219 | max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
220 | });
221 | }
222 |
223 |
224 | this.scale = new Chart.Scale(scaleOptions);
225 | },
226 | addData : function(valuesArray,label){
227 | //Map the values array for each of the datasets
228 |
229 | helpers.each(valuesArray,function(value,datasetIndex){
230 | //Add a new point for each piece of data, passing any required data to draw.
231 | this.datasets[datasetIndex].points.push(new this.PointClass({
232 | value : value,
233 | label : label,
234 | datasetLabel: this.datasets[datasetIndex].label,
235 | x: this.scale.calculateX(this.scale.valuesCount+1),
236 | y: this.scale.endPoint,
237 | strokeColor : this.datasets[datasetIndex].pointStrokeColor,
238 | fillColor : this.datasets[datasetIndex].pointColor
239 | }));
240 | },this);
241 |
242 | this.scale.addXLabel(label);
243 | //Then re-render the chart.
244 | this.update();
245 | },
246 | removeData : function(){
247 | this.scale.removeXLabel();
248 | //Then re-render the chart.
249 | helpers.each(this.datasets,function(dataset){
250 | dataset.points.shift();
251 | },this);
252 | this.update();
253 | },
254 | reflow : function(){
255 | var newScaleProps = helpers.extend({
256 | height : this.chart.height,
257 | width : this.chart.width
258 | });
259 | this.scale.update(newScaleProps);
260 | },
261 | draw : function(ease){
262 | var easingDecimal = ease || 1;
263 | this.clear();
264 |
265 | var ctx = this.chart.ctx;
266 |
267 | // Some helper methods for getting the next/prev points
268 | var hasValue = function(item){
269 | return item.value !== null;
270 | },
271 | nextPoint = function(point, collection, index){
272 | return helpers.findNextWhere(collection, hasValue, index) || point;
273 | },
274 | previousPoint = function(point, collection, index){
275 | return helpers.findPreviousWhere(collection, hasValue, index) || point;
276 | };
277 |
278 | if (!this.scale) return;
279 | this.scale.draw(easingDecimal);
280 |
281 |
282 | helpers.each(this.datasets,function(dataset){
283 | var pointsWithValues = helpers.where(dataset.points, hasValue);
284 |
285 | //Transition each point first so that the line and point drawing isn't out of sync
286 | //We can use this extra loop to calculate the control points of this dataset also in this loop
287 |
288 | helpers.each(dataset.points, function(point, index){
289 | if (point.hasValue()){
290 | point.transition({
291 | y : this.scale.calculateY(point.value),
292 | x : this.scale.calculateX(index)
293 | }, easingDecimal);
294 | }
295 | },this);
296 |
297 |
298 | // Control points need to be calculated in a separate loop, because we need to know the current x/y of the point
299 | // This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed
300 | if (this.options.bezierCurve){
301 | helpers.each(pointsWithValues, function(point, index){
302 | var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0;
303 | point.controlPoints = helpers.splineCurve(
304 | previousPoint(point, pointsWithValues, index),
305 | point,
306 | nextPoint(point, pointsWithValues, index),
307 | tension
308 | );
309 |
310 | // Prevent the bezier going outside of the bounds of the graph
311 |
312 | // Cap puter bezier handles to the upper/lower scale bounds
313 | if (point.controlPoints.outer.y > this.scale.endPoint){
314 | point.controlPoints.outer.y = this.scale.endPoint;
315 | }
316 | else if (point.controlPoints.outer.y < this.scale.startPoint){
317 | point.controlPoints.outer.y = this.scale.startPoint;
318 | }
319 |
320 | // Cap inner bezier handles to the upper/lower scale bounds
321 | if (point.controlPoints.inner.y > this.scale.endPoint){
322 | point.controlPoints.inner.y = this.scale.endPoint;
323 | }
324 | else if (point.controlPoints.inner.y < this.scale.startPoint){
325 | point.controlPoints.inner.y = this.scale.startPoint;
326 | }
327 | },this);
328 | }
329 |
330 |
331 | //Draw the line between all the points
332 | ctx.lineWidth = this.options.datasetStrokeWidth;
333 | ctx.strokeStyle = dataset.strokeColor;
334 | ctx.beginPath();
335 |
336 | helpers.each(pointsWithValues, function(point, index){
337 | if (index === 0){
338 | ctx.moveTo(point.x, point.y);
339 | }
340 | else{
341 | if(this.options.bezierCurve){
342 | var previous = previousPoint(point, pointsWithValues, index);
343 |
344 | ctx.bezierCurveTo(
345 | previous.controlPoints.outer.x,
346 | previous.controlPoints.outer.y,
347 | point.controlPoints.inner.x,
348 | point.controlPoints.inner.y,
349 | point.x,
350 | point.y
351 | );
352 | }
353 | else{
354 | ctx.lineTo(point.x,point.y);
355 | }
356 | }
357 | }, this);
358 |
359 | if (this.options.datasetStroke) {
360 | ctx.stroke();
361 | }
362 |
363 | if (this.options.datasetFill && pointsWithValues.length > 0){
364 | //Round off the line by going to the base of the chart, back to the start, then fill.
365 | ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint);
366 | ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint);
367 | ctx.fillStyle = dataset.fillColor;
368 | ctx.closePath();
369 | ctx.fill();
370 | }
371 |
372 | //Now draw the points over the line
373 | //A little inefficient double looping, but better than the line
374 | //lagging behind the point positions
375 | helpers.each(pointsWithValues,function(point){
376 | point.draw();
377 | });
378 | },this);
379 | }
380 | });
381 |
382 |
383 | }).call(this);
384 |
--------------------------------------------------------------------------------
/JS/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: arial;
3 | font-size: 12px;
4 | text-align: center;
5 | margin: 0;
6 | ;
7 | width: 100%;
8 | }
9 |
10 | .clear {
11 | clear: both;
12 | }
13 |
14 | fieldset {
15 | height: 90%;
16 | }
17 |
18 | fieldset#playerName {
19 | width: 80px;
20 | float: left;
21 | height: 180px;
22 | }
23 |
24 | fieldset#gamemode {
25 | width: 60px;
26 | float: left;
27 | }
28 |
29 | fieldset#zeropointsetting {
30 | width: 60px;
31 | float: left;
32 | }
33 |
34 | #gamearea {
35 | /*background:待添加*/
36 | width: 500px;
37 | height: 500px;
38 | border: 3px solid #0F0;
39 | border-radius: 10px;
40 | position: relative;
41 | float: left;
42 | }
43 |
44 | .playerinfoarea {
45 | width: 31%;
46 | height: 31%;
47 | border: 3px solid black;
48 | border-radius: 3px;
49 | position: absolute;
50 | text-align: left;
51 | }
52 |
53 | #player1 {
54 | left: 35%;
55 | top: 69%;
56 | }
57 |
58 | #player2 {
59 | left: 69%;
60 | top: 35%;
61 | }
62 |
63 | #player3 {
64 | left: 35%;
65 | }
66 |
67 | #player4 {
68 | top: 35%;
69 | }
70 |
71 | .playerpos {
72 | font-family: 'Tangerine', serif;
73 | font-size: 30px;
74 | text-shadow: 4px 4px 4px #aaa;
75 | text-align: right;
76 | float: left;
77 | }
78 |
79 | .playername {
80 | font-family: 'Droid Serif', serif;
81 | font-size: 30px;
82 | float: right;
83 | width: 100px
84 | }
85 |
86 | .playerscore {
87 | font-family: "Comic Sans MS";
88 | font-size: 24px;
89 | }
90 |
91 | .playerscorediff {
92 | width: auto;
93 | height: 18px;
94 | font-size: 15px;
95 | text-align: center;
96 | background: url(img/lichi.gif) no-repeat;
97 | background-size: 100% 100%;
98 | }
99 |
100 | .weici {
101 | font-size: 24px;
102 | text-align: center;
103 | }
104 |
105 | #xiru_field {
106 | float: left;
107 | width: 60px;
108 | }
109 |
110 | #game_process {
111 | width: 33%;
112 | height: 100px;
113 | float: right;
114 | font-size: 24px;
115 | text-align: left;
116 | border: 2px solid #80abde;
117 | border-radius: 0 0 0 20px;
118 | }
119 |
120 | .inline_text {
121 | display: inline;
122 | color: forestgreen;
123 | }
124 |
125 | .rong_btn {
126 | width: 45%;
127 | font-size: 28px;
128 | height: 40px;
129 | border: 2px #9999FF dashed;
130 | background-color: red;
131 | -webkit-appearance: none;
132 | bottom: 0px;
133 | position: absolute;
134 | left: 0px;
135 | }
136 |
137 | .dianpao_btn {
138 | width: 45%;
139 | font-size: 28px;
140 | height: 40px;
141 | background-color: pink;
142 | border: 2px #9999FF dashed;
143 | -webkit-appearance: none;
144 | bottom: 0px;
145 | position: absolute;
146 | right: 0px;
147 | }
148 |
149 | .t_btn_click {
150 | color: aliceblue;
151 | background-color: black;
152 | border: 5px solid #F0e;
153 | border-radius: 10px;
154 | //font-size: 30px;
155 | height: 50px;
156 | }
157 |
158 | .mainviewcl {
159 | color: dodgerblue;
160 | }
161 |
162 | .corner_btn {
163 | float: right;
164 | position: absolute;
165 | width: 100px;
166 | height: 40px;
167 | border: 2px dashed green;
168 | font-size: 20px;
169 | right: 5px;
170 | -webkit-appearance: none;
171 | }
172 |
173 | .corner_btn_4win {
174 | bottom: 50px;
175 | }
176 |
177 | .end99 {
178 | bottom: 5px;
179 | }
180 |
181 | .end4win {
182 | bottom: 50px;
183 | -webkit-appearance: none;
184 | }
185 |
186 | .lichi {
187 | border: 5px dashed #F00;
188 | border-radius: 15px;
189 | }
190 |
191 | #cal_area {
192 | margin-left: 30px;
193 | border: 2px solid #000;
194 | border-radius: 20px;
195 | margin-left: 510px;
196 | margin-right: 2px;
197 | padding: 5px;
198 | //max-width: 500px;
199 | }
200 |
201 | #fanshu_field {
202 | float: left;
203 | width: 70px;
204 | }
205 |
206 | #fushu_field {
207 | float: left;
208 | width: 70px;
209 | }
210 |
211 | .fanfu_btn {
212 | width: 70px;
213 | height: 25px;
214 | margin-top: 5px;
215 | margin-bottom: 5px;
216 | border-left: 1px solid #C0C0C0;
217 | border-right: 1px solid #6C6C6C;
218 | border-top: 1px solid #C0C0C0;
219 | border-bottom: 1px solid #6C6C6C;
220 | background: white;
221 | border-radius: 8px;
222 | }
223 |
224 | .fu_btn {
225 | width: 80%;
226 | margin-top: 5px;
227 | margin-bottom: 5px;
228 | border-left: 1px solid #C0C0C0;
229 | border-right: 1px solid #6C6C6C;
230 | border-top: 1px solid #C0C0C0;
231 | border-bottom: 1px solid #6C6C6C;
232 | background: white;
233 | border-radius: 8px;
234 | }
235 |
236 | .fanfu_help_btn {
237 | width: 90%;
238 | height: 40px;
239 | margin: 3px;
240 | border-left: 1px solid #C0C0C0;
241 | border-right: 1px solid #6C6C6C;
242 | border-top: 1px solid #C0C0C0;
243 | border-bottom: 1px solid #6C6C6C;
244 | background: white;
245 | font-size: 18px;
246 | border-radius: 8px;
247 | }
248 |
249 | .fanshu_clk {
250 | border: 2px dashed #F00;
251 | }
252 |
253 | .fushu_clk {
254 | border: 2px dashed #F00;
255 | }
256 |
257 | .fanfu_help_clk {
258 | border: 2px dashed #F00;
259 | width: 90%;
260 | font-size: 18px;
261 | }
262 |
263 | .mz_cal_clk {
264 | border: 2px dashed #F00;
265 | width: 60%;
266 | }
267 |
268 | .set_fu {
269 | width: 150px;
270 | height: 30px;
271 | margin: 3px;
272 | border-left: 1px solid #C0C0C0;
273 | border-right: 1px solid #6C6C6C;
274 | border-top: 1px solid #C0C0C0;
275 | border-bottom: 1px solid #6C6C6C;
276 | background: white;
277 | font-size: 18px;
278 | border-radius: 8px;
279 | }
280 |
281 | .fanshu_ok_btn {
282 | width: 80%;
283 | height: 40px;
284 | margin: 3px;
285 | border-left: 1px solid #C0C0C0;
286 | border-right: 1px solid #6C6C6C;
287 | border-top: 1px solid #C0C0C0;
288 | border-bottom: 1px solid #6C6C6C;
289 | background: white;
290 | font-size: 30px;
291 | border-radius: 8px;
292 | }
293 |
294 | #liuju {
295 | bottom: 0px;
296 | position: absolute;
297 | }
298 |
299 | #liuju_btn {
300 | width: 60px;
301 | height: 60px;
302 | margin: 5px;
303 | border-left: 1px solid #C0C0C0;
304 | border-right: 1px solid #6C6C6C;
305 | border-top: 1px solid #C0C0C0;
306 | border-bottom: 1px solid #6C6C6C;
307 | background: green;
308 | border-radius: 20px;
309 | font-size: 36px;
310 | -webkit-appearance: none;
311 | }
312 |
313 | .tab_cls {
314 | height: 390px;
315 | }
316 |
317 | .liuju_icon {
318 | border: 4px solid grey;
319 | border-radius: 40px;
320 | position: absolute;
321 | font-size: 80px;
322 | color: grey;
323 | right: -40px;
324 | top: -40px;
325 | z-index: 100;
326 | font-family: cursive;
327 | line-height: 100%;
328 | margin: 20px;
329 | -webkit-transform: rotate(-30deg);
330 | -moz-transform: rotate(-30deg);
331 | -o-transform: rotate(-30deg);
332 | -ms-transform: rotate(-30deg);
333 | transform: rotate(-30deg);
334 | opacity: 0.4;
335 | filter: alpha(opacity=40);
336 | visibility: hidden;
337 | }
338 |
339 | .liuju_icon_inner {
340 | position: relative;
341 | left: -9px;
342 | }
343 |
344 | .liuju_noting {
345 | border: 4px solid grey;
346 | color: grey;
347 | }
348 |
349 | .liuju_ting {
350 | border: 4px solid crimson;
351 | color: crimson;
352 | opacity: 0.9;
353 | filter: alpha(opacity=90);
354 | }
355 |
356 | #recovery_btn {
357 | width: 60px;
358 | height: 60px;
359 | margin: 5px;
360 | border-left: 1px solid #C0C0C0;
361 | border-right: 1px solid #6C6C6C;
362 | border-top: 1px solid #C0C0C0;
363 | border-bottom: 1px solid #6C6C6C;
364 | border-radius: 20px;
365 | font-size: 36px;
366 | -webkit-appearance: none;
367 | }
368 |
369 | .player_name_edit {
370 | width: 70px;
371 | }
372 |
373 | button {
374 | font-size: 0;
375 | padding: 0;
376 | -webkit-appearance: none;
377 | }
378 |
379 | button span {
380 | font-size: 36px;
381 | margin: 0 -1em;
382 | padding: 0.4em 0.8em;
383 | }
384 |
385 | button.rong_btn span {
386 | font-size: 28px;
387 | margin: 0 -1em;
388 | padding: 0.4em 0.8em;
389 | }
390 |
391 | button.dianpao_btn span {
392 | font-size: 28px;
393 | margin: 0 -1em;
394 | padding: 0.4em 0.8em;
395 | }
396 |
397 | button.corner_btn span {
398 | font-size: 20px;
399 | margin: 0 -1em;
400 | padding: 0.4em 0.8em;
401 | }
402 |
403 | .setting_btn {
404 | float: left;
405 | display: block;
406 | width: 100px;
407 | height: 40px;
408 | border-left: 2px solid #C0C0C0;
409 | border-right: 2px solid #6C6C6C;
410 | border-top: 2px solid #C0C0C0;
411 | border-bottom: 2px solid #6C6C6C;
412 | background: white;
413 | margin: 5px;
414 | border-radius: 20px;
415 | }
416 |
417 | button.setting_btn span {
418 | font-size: 20px;
419 | margin: 0 -1em;
420 | padding: 0.4em 0.8em;
421 | }
422 |
423 | .playerscore_animate {
424 | position: absolute;
425 | font-size: 40px;
426 | color: firebrick;
427 | top: 40px;
428 | left: 30px;
429 | }
430 |
431 | .dice {
432 | position: relative;
433 | float: left;
434 | width: 45px;
435 | height: 45px;
436 | background: url("img/dice.png");
437 | background-size: 320px, 320px;
438 | background-repeat: no-repeat;
439 | background-position: -0px;
440 | margin-left: 80px;
441 | margin-top: 20px;
442 | }
443 |
444 | #dice_area {
445 | position: relative;
446 | float: left;
447 | height: 50px;
448 | width: 250px;
449 | }
450 |
451 | #myTab0_Content2 {
452 | text-align: left;
453 | font-size: 15px;
454 | }
455 |
456 | .fan_num_desc {
457 | display: inline;
458 | float: left;
459 | margin: 0px;
460 | color: blue
461 | }
462 |
463 | .fan_desc {
464 | float: left;
465 | width: 303px;
466 | }
467 |
468 | .red_star {
469 | color: red;
470 | display: inline;
471 | }
472 |
473 | #chartContainer {
474 | position: absolute;
475 | width: 37%;
476 | height: 37%;
477 | left: 32%;
478 | top: 32%;
479 | // border:1px solid #f00;
480 | }
481 |
482 | #linechartContainer {
483 | float: left;
484 | margin-left: 30px;
485 | }
486 |
487 | #expand_panel_btn {
488 | margin-top: 10px;
489 | float: left;
490 | width: 30px;
491 | height: 400px;
492 | background: white;
493 | border-left: 1px solid #C0C0C0;
494 | border-right: 1px solid #6C6C6C;
495 | border-top: 1px solid #C0C0C0;
496 | border-bottom: 1px solid #6C6C6C;
497 | border-radius: 10px;
498 | position: absolute;
499 | left: 0px;
500 | }
501 |
502 | #myTab0_Content3 {
503 | font-size: 15px;
504 | }
--------------------------------------------------------------------------------
/JS/support.js:
--------------------------------------------------------------------------------
1 | function $q(pattern, idx) { //jQuery辅助函数
2 | return $(pattern + ":eq(" + idx + ')');
3 | }
4 |
5 | function clone(myObj) { //deep copy of object
6 | if (typeof (myObj) != 'object' || myObj == null) return myObj;
7 | var newObj = new Object();
8 | for (var i in myObj) {
9 | newObj[i] = clone(myObj[i]);
10 | }
11 | return newObj;
12 | }
13 |
14 | function randomOrder(targetArray) {
15 | var arrayLength = targetArray.length
16 | var tempArray1 = new Array();
17 | for (var i = 0; i < arrayLength; i++) {
18 | tempArray1[i] = i
19 | }
20 | var tempArray2 = new Array();
21 | for (var i = 0; i < arrayLength; i++) {
22 | tempArray2[i] = tempArray1.splice(Math.floor(Math.random() * tempArray1.length), 1)
23 | }
24 | var tempArray3 = new Array();
25 | for (var i = 0; i < arrayLength; i++) {
26 | tempArray3[i] = targetArray[tempArray2[i]]
27 | }
28 | return tempArray3
29 | }
--------------------------------------------------------------------------------
/JS/switch.css:
--------------------------------------------------------------------------------
1 | .onoffswitch {
2 | position: relative;
3 | float:left;
4 | width: 90px;
5 | -webkit-user-select:none;
6 | -moz-user-select:none;
7 | -ms-user-select: none;
8 | }
9 | .onoffswitch-checkbox {
10 | display: none;
11 | }
12 | .onoffswitch-label {
13 | display: block; overflow: hidden; cursor: pointer;
14 | border: 2px solid #999999; border-radius: 20px;
15 | }
16 | .onoffswitch-inner {
17 | display: block;
18 | width: 200%;
19 | margin-left: -100%;
20 | transition: margin 0.3s ease-in 0s;
21 | }
22 | .onoffswitch-inner:before, .onoffswitch-inner:after {
23 | display: block; float: left; width: 50%; height: 30px; padding: 0; line-height: 30px;
24 | font-size: 14px; color: white; font-family: Trebuchet, Arial, sans-serif; font-weight: bold;
25 | box-sizing: border-box;
26 | }
27 | .onoffswitch-inner:before {
28 | content: "点数";
29 | background-color: #34A7C1; color: #FFFFFF;
30 | }
31 | .onoffswitch-inner:after {
32 | content: "点差";
33 | padding-right: 20px;
34 | background-color: #EEEEEE; color: #999999;
35 | text-align: right;
36 | }
37 | .onoffswitch-switch {
38 | display: block; width: 18px; margin: 6px;
39 | background: #FFFFFF;
40 | position: absolute; top: 0; bottom: 0;
41 | right: 56px;
42 | border: 2px solid #999999; border-radius: 20px;
43 | transition: all 0.3s ease-in 0s;
44 | }
45 | .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
46 | margin-left: 0;
47 | }
48 | .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
49 | right: 0px;
50 | }
--------------------------------------------------------------------------------
/JS/tab.js:
--------------------------------------------------------------------------------
1 | function nTabs(thisObj, Num) {
2 | if (thisObj.className == "active") return;
3 | var tabObj = thisObj.parentNode.id;
4 | var tabList = document.getElementById(tabObj).getElementsByTagName("li");
5 | for (var i = 0; i < tabList.length; i++) {
6 | if (i == Num) {
7 | thisObj.className = "active";
8 | document.getElementById(tabObj + "_Content" + i).style.display = "block";
9 | } else {
10 | tabList[i].className = "normal";
11 | document.getElementById(tabObj + "_Content" + i).style.display = "none";
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/JS/tabstyle.css:
--------------------------------------------------------------------------------
1 | .nTab {
2 | float: left;
3 | position: relative;
4 | margin: 0 auto;
5 | border-bottom: 1px #C7C7CD solid;
6 | /*background: #d5d5d5;*/
7 | background-position: left;
8 | background-repeat: repeat-y;
9 | margin-bottom: 2px;
10 | left:-0px;
11 | }
12 | .nTab .TabTitle {
13 | clear: both;
14 | height: 26px;
15 | overflow: hidden;
16 | }
17 | .nTab .TabTitle ul {
18 | margin: 0;
19 | padding: 0;
20 | }
21 | .nTab .TabTitle li {
22 | float: left;
23 | width: 94px;
24 | cursor: pointer;
25 | padding-top: 6px;
26 | padding-right: 0px;
27 | padding-left: 0px;
28 | padding-bottom: 7px;
29 | list-style-type: none;
30 | }
31 | .nTab .TabTitle .active {
32 | background: url(./img/tab_bg1.gif) left -25px no-repeat;
33 | border-left: 1px #C7C7CD solid;
34 | border-top: 1px #C7C7CD solid;
35 | border-bottom: 1px #fff solid;
36 | }
37 | .nTab .TabTitle .normal {
38 | background: url(./img/tab_bg1.gif);
39 | border-top: 1px #C7C7CD solid;
40 | border-bottom: 1px #C7C7CD solid;
41 | }
42 | .nTab .TabContent {
43 | width: auto;
44 | background: #fff;
45 | margin: 0px auto;
46 |
47 | border-right: 1px #C7C7CD solid;
48 | border-left: 1px #C7C7CD solid;
49 | }
50 | .none {
51 | display: none;
52 | }
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Mahosyojyo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Majong-AutoScore
2 | ================
3 |
4 | 日麻自动算分器
5 |
6 | #开坑申明
7 |
8 | 目标:实现一个手麻日麻辅助软件。
9 |
10 | 主要是两个版本
11 |
12 | * JS版本,由Vespa完成
13 | * iOS版本,由Fredfx完成
14 | * Android版本,再说吧,呵呵
15 |
16 | 人员:
17 | * Vespa
18 | * Fredfx
19 | * Yuki(测试阶段使用用户)
20 |
21 | ================
22 | ###文件说明:
23 | >./iOS/:
24 | >iOS版
25 |
26 | >./Andriod/:
27 | >安卓版
28 |
29 | >./JS/:
30 | >Js版本
31 |
32 | >./Design.md:
33 | >设计思路和功能完善
34 |
--------------------------------------------------------------------------------
/iOS/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/.DS_Store
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/.DS_Store
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator.xcodeproj/project.xcworkspace/xcuserdata/fredfx.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator.xcodeproj/project.xcworkspace/xcuserdata/fredfx.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator.xcodeproj/xcuserdata/fredfx.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator.xcodeproj/xcuserdata/fredfx.xcuserdatad/xcschemes/MahjongScoreCalculator.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator.xcodeproj/xcuserdata/fredfx.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MahjongScoreCalculator.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | C8E211FD1C3817160007CAA7
16 |
17 | primary
18 |
19 |
20 | C8E212161C3817160007CAA7
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/.DS_Store
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 | @end
16 |
17 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "MMSCPlayerNameAndSeatController.h"
11 |
12 | @interface AppDelegate ()
13 |
14 | @end
15 |
16 | @implementation AppDelegate
17 |
18 |
19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
20 |
21 | MMSCPlayerNameAndSeatController *nameAndSeatController = [[MMSCPlayerNameAndSeatController alloc] init];
22 |
23 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
24 | [self.window setBackgroundColor:[UIColor clearColor]];
25 | self.window.rootViewController = nameAndSeatController;
26 | [self.window makeKeyAndVisible];
27 |
28 | return YES;
29 | }
30 |
31 | - (void)applicationWillResignActive:(UIApplication *)application {
32 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
33 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
34 | }
35 |
36 | - (void)applicationDidEnterBackground:(UIApplication *)application {
37 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
38 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
39 | }
40 |
41 | - (void)applicationWillEnterForeground:(UIApplication *)application {
42 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
43 | }
44 |
45 | - (void)applicationDidBecomeActive:(UIApplication *)application {
46 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
47 | }
48 |
49 | - (void)applicationWillTerminate:(UIApplication *)application {
50 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
51 | }
52 |
53 | @end
54 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x",
7 | "filename" : "Icon-Small@2x.png"
8 | },
9 | {
10 | "idiom" : "iphone",
11 | "size" : "29x29",
12 | "scale" : "3x",
13 | "filename" : "Icon-Small@3x.png"
14 | },
15 | {
16 | "idiom" : "iphone",
17 | "size" : "40x40",
18 | "scale" : "2x",
19 | "filename" : "Icon-40@2x.png"
20 | },
21 | {
22 | "idiom" : "iphone",
23 | "size" : "40x40",
24 | "scale" : "3x",
25 | "filename" : "Icon-40@3x.png"
26 | },
27 | {
28 | "idiom" : "iphone",
29 | "size" : "60x60",
30 | "scale" : "2x",
31 | "filename" : "Icon-60@2x.png"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "3x",
37 | "filename" : "Icon-60@3x.png"
38 | },
39 | {
40 | "idiom" : "ipad",
41 | "size" : "29x29",
42 | "scale" : "1x",
43 | "filename" : "Icon-Small.png"
44 | },
45 | {
46 | "idiom" : "ipad",
47 | "size" : "29x29",
48 | "scale" : "2x",
49 | "filename" : "Icon-Small@2x.png"
50 | },
51 | {
52 | "idiom" : "ipad",
53 | "size" : "40x40",
54 | "scale" : "1x",
55 | "filename" : "Icon-40.png"
56 | },
57 | {
58 | "idiom" : "ipad",
59 | "size" : "40x40",
60 | "scale" : "2x",
61 | "filename" : "Icon-40@2x.png"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "76x76",
66 | "scale" : "1x",
67 | "filename" : "Icon-76.png"
68 | },
69 | {
70 | "idiom" : "ipad",
71 | "size" : "76x76",
72 | "scale" : "2x",
73 | "filename" : "Icon-76@2x.png"
74 | },
75 | {
76 | "idiom" : "ipad",
77 | "size" : "83.5x83.5",
78 | "scale" : "2x",
79 | "filename" : "Icon-83.5@2x.png"
80 | }
81 | ],
82 | "info" : {
83 | "version" : 1,
84 | "author" : "makeappicon"
85 | }
86 | }
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-40.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-76.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/Brand Assets.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "portrait",
5 | "idiom" : "iphone",
6 | "minimum-system-version" : "7.0",
7 | "scale" : "2x"
8 | },
9 | {
10 | "orientation" : "portrait",
11 | "idiom" : "iphone",
12 | "minimum-system-version" : "7.0",
13 | "subtype" : "retina4",
14 | "scale" : "2x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/MMSCGame.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCGame.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MMSCRound.h"
11 |
12 | @class MMSCRoundResult;
13 | @class MMSCPlayer;
14 |
15 | @interface MMSCGame : NSObject
16 |
17 | // 玩家们
18 | @property(nonatomic, strong) NSArray *players;
19 | // 庄家索引
20 | @property(nonatomic, assign) NSInteger oyaIndex;
21 | // 对局们
22 | @property(nonatomic, strong) NSMutableArray *rounds;
23 |
24 | // 初始化游戏
25 | - (void)gameInit;
26 |
27 | // 随机座位
28 | - (void)randomSeat;
29 |
30 | // 结束该局
31 | - (void)endCurrentRoundWithResult:(MMSCRoundResult *)result;
32 |
33 | // 判断对局是否结束
34 | - (BOOL)isGameEnded;
35 |
36 | // 立直
37 | - (void)richiAtPlayerIndex:(NSUInteger)playerIndex;
38 |
39 | // 取消立直
40 | - (void)cancelRichiAtPlayerIndex:(NSUInteger)playerIndex;
41 |
42 | // 获取庄家
43 | - (MMSCPlayer *)getOYA;
44 |
45 | // 获取某个玩家
46 | - (MMSCPlayer *)getPlayerAtIndex:(NSInteger)index;
47 |
48 | // 获取当前的一局
49 | - (MMSCRound *)currentRound;
50 |
51 | // 排位
52 | - (NSArray *)playerRank;
53 |
54 | // 撤销上一局
55 | - (void)cancelLastRound;
56 |
57 | @end
58 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/MMSCGame.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCGame.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCGame.h"
10 | #import "MMSCRound.h"
11 | #import "MMSCRoundResult.h"
12 |
13 | @implementation MMSCGame
14 |
15 | - (void)randomSeat {
16 |
17 | NSMutableArray *temp = [NSMutableArray arrayWithArray:self.players];
18 |
19 | for (int i = 0; i < 4; i++) {
20 | int index = rand() % (4 - i);
21 |
22 | [temp exchangeObjectAtIndex:i withObjectAtIndex:i + index];
23 | }
24 |
25 | self.players = [NSArray arrayWithArray:temp];
26 |
27 | [self gameInit];
28 | }
29 |
30 | - (void)gameInit {
31 |
32 | self.oyaIndex = 0;
33 |
34 | //分配下风位
35 | for (int i = 0; i < 4; i++) {
36 | MMSCPlayer *player = (MMSCPlayer *)self.players[i];
37 | player.wind = (MMSCWind)(MMSCWindEast + i);
38 | }
39 |
40 | self.rounds = [NSMutableArray array];
41 | MMSCRound *firstRound = [MMSCRound new];
42 | firstRound.roundWind = MMSCWindEast;
43 | firstRound.roundNumeber = 1;
44 |
45 | [self.rounds addObject:firstRound];
46 | }
47 |
48 | - (void)endCurrentRoundWithResult:(MMSCRoundResult *)result {
49 |
50 | MMSCRound *currentRound = [self currentRound];
51 | [currentRound setResult:result];
52 |
53 | // 结束该局,分配分数
54 | [currentRound endRoundWithPlayers:self.players];
55 |
56 | if (![self isGameEnded]) {
57 | [self setNextRound:currentRound];
58 | }
59 | }
60 |
61 | - (void)setNextRound:(MMSCRound *)currentRound {
62 | MMSCRoundResult *currentResult = [currentRound result];
63 | MMSCRound *nextRound = [MMSCRound new];
64 |
65 | BOOL isDraw = [currentResult isDraw];
66 | BOOL changeOYA = [currentResult changeOYA:self.oyaIndex];
67 | currentRound.oyaChanged = changeOYA;
68 |
69 | // 设置场风和局数,换庄
70 | if (!changeOYA) {
71 | nextRound.roundWind = currentRound.roundWind;
72 | nextRound.roundNumeber = currentRound.roundNumeber;
73 | } else {
74 | [self setNextOYAAndPlayersWind];
75 | [self setNextWindAndRoundNumber:nextRound currentRound:currentRound];
76 | }
77 |
78 | // 本场数
79 | if (!changeOYA || isDraw) {
80 | nextRound.bonbanNumber = currentRound.bonbanNumber + 1;
81 | }
82 |
83 | // 立直棒处理
84 | if (isDraw) {
85 | nextRound.richiScore = currentRound.richiScore;
86 | }
87 |
88 | [self.rounds addObject:nextRound];
89 | }
90 |
91 | - (void)setNextOYAAndPlayersWind {
92 |
93 | self.oyaIndex++;
94 |
95 | if (self.oyaIndex >= 4) {
96 | self.oyaIndex = 0;
97 | }
98 |
99 | for (MMSCPlayer *player in self.players) {
100 | player.wind = [self previousWind:player.wind];
101 | }
102 | }
103 |
104 | - (void)setPreviousOYAAndPlayersWind {
105 | self.oyaIndex--;
106 |
107 | if (self.oyaIndex < 0) {
108 | self.oyaIndex = 3;
109 | }
110 |
111 | for (MMSCPlayer *player in self.players) {
112 | player.wind = [self nextWind:player.wind];
113 | }
114 | }
115 |
116 | - (void)setNextWindAndRoundNumber:(MMSCRound *)nextRound currentRound:(MMSCRound *)currentRound {
117 | NSInteger nextRoundNumber = currentRound.roundNumeber + 1;
118 | MMSCWind nextRoundWind = currentRound.roundWind;
119 |
120 | if (nextRoundNumber > 4) {
121 | nextRoundWind = [self nextWind:nextRoundWind];
122 | nextRoundNumber = 1;
123 | }
124 |
125 | nextRound.roundWind = nextRoundWind;
126 | nextRound.roundNumeber = nextRoundNumber;
127 | }
128 |
129 | - (MMSCWind)nextWind:(MMSCWind)currentWind {
130 | MMSCWind result = currentWind + 1;
131 |
132 | if (result > MMSCWindNorth) {
133 | result = MMSCWindEast;
134 | }
135 |
136 | return result;
137 | }
138 |
139 | - (MMSCWind)previousWind:(MMSCWind)currentWind {
140 | MMSCWind result = currentWind - 1;
141 |
142 | if (result < 0) {
143 | result = MMSCWindNorth;
144 | }
145 |
146 | return result;
147 | }
148 |
149 | - (BOOL)isGameEnded {
150 |
151 | __block NSInteger topIndex = 0;
152 | __block BOOL result = NO;
153 |
154 | [self.players enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
155 | MMSCPlayer *player = (MMSCPlayer *)obj;
156 |
157 | if (player.score < 0) {
158 | result = YES;
159 | *stop = YES;
160 | return;
161 | }
162 |
163 | MMSCPlayer *topPlayer = (MMSCPlayer *)self.players[topIndex];
164 | if (player.score >= topPlayer.score) {
165 | topIndex = idx;
166 | }
167 | }];
168 |
169 | if (result) { // 有人被飞
170 | return YES;
171 | }
172 |
173 | MMSCRound *currentRound = [self currentRound];
174 | MMSCPlayer *topPlayer = (MMSCPlayer *)self.players[topIndex];
175 | BOOL isAllLast = (currentRound.roundWind == MMSCWindSouth && currentRound.roundNumeber >= 4) || currentRound.roundWind > MMSCWindSouth;
176 |
177 | if (!isAllLast || (isAllLast && topPlayer.score < 30000)) { // 不是all last 或者 没人上三万分(可能要西入)
178 | return NO;
179 | }
180 |
181 | if ([currentRound.result changeOYA:self.oyaIndex]) {
182 | return YES;
183 | }
184 |
185 | return topIndex != self.oyaIndex;
186 | }
187 |
188 | - (void)richiAtPlayerIndex:(NSUInteger)playerIndex {
189 | MMSCRound *currentRound = [self currentRound];
190 | NSAssert(playerIndex < 4, @"这是什么鬼?!");
191 |
192 | MMSCPlayer *richiPlayer = (MMSCPlayer *)self.players[playerIndex];
193 | [currentRound richiAtPlayer:richiPlayer index:playerIndex];
194 | }
195 |
196 | - (void)cancelRichiAtPlayerIndex:(NSUInteger)playerIndex {
197 | MMSCRound *currentRound = [self currentRound];
198 | NSAssert(playerIndex < 4, @"这是什么鬼?!");
199 |
200 | MMSCPlayer *richiPlayer = (MMSCPlayer *)self.players[playerIndex];
201 | [currentRound cancelRichiAtPlayer:richiPlayer index:playerIndex];
202 | }
203 |
204 | - (MMSCPlayer *)getOYA {
205 | return (MMSCPlayer *)self.players[self.oyaIndex];
206 | }
207 |
208 | - (MMSCPlayer *)getPlayerAtIndex:(NSInteger)index {
209 | return (MMSCPlayer *)self.players[index];
210 | }
211 |
212 | - (MMSCRound *)currentRound {
213 | return [self.rounds lastObject];
214 | }
215 |
216 | - (NSArray *)playerRank {
217 | NSArray *sortedPlayers = [self.players sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
218 | MMSCPlayer *player1 = (MMSCPlayer *)obj1;
219 | MMSCPlayer *player2 = (MMSCPlayer *)obj2;
220 |
221 | if (player1.score == player2.score) {
222 | return NSOrderedSame;
223 | }
224 |
225 | if (player1.score < player2.score) {
226 | return NSOrderedDescending;
227 | }
228 |
229 | return NSOrderedAscending;
230 | }];
231 |
232 | return sortedPlayers;
233 | }
234 |
235 | - (void)cancelLastRound {
236 | if (![self isGameEnded]) {
237 | // 当前未完的一局
238 | MMSCRound *currentRound = [self currentRound];
239 | [self.rounds removeObject:currentRound];
240 | }
241 |
242 | // 真正要取消的一局
243 | MMSCRound *roundToCancel = [self currentRound];
244 | [self.rounds removeObject:roundToCancel];
245 |
246 | // 恢复分数
247 | NSArray *scoreChanges = [roundToCancel roundScoreChanges];
248 | for (int i = 0; i < 4; i++) {
249 | NSInteger scoreChange = [scoreChanges[i] integerValue];
250 | MMSCPlayer *player = self.players[i];
251 | [player decreaseScore:scoreChange];
252 | }
253 |
254 | // 恢复风位
255 | BOOL changeOYA = roundToCancel.oyaChanged;
256 | if (changeOYA) {
257 | [self setPreviousOYAAndPlayersWind];
258 | }
259 | MMSCRound *currentRound = [self currentRound];
260 | if (!currentRound) {
261 | [self gameInit];
262 | return;
263 | }
264 | if (currentRound.oyaChanged) { // 先恢复到结算之前的状态
265 | [self setPreviousOYAAndPlayersWind];
266 | }
267 |
268 | [self setNextRound:currentRound]; // 重新结算下风位
269 | }
270 |
271 | @end
272 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/MMSCGameManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCGameManager.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/2/11.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MMSCGameManager : NSObject
12 |
13 | + (MMSCGameManager *)instance;
14 |
15 | // 初始化
16 | - (void)initGameWithPlayers:(NSArray *)playerNames;
17 |
18 | // 随机座位,初始化并返回结果
19 | - (NSArray *)randomSeatWithPlayers:(NSArray *)playerNames;
20 |
21 | // 局数
22 | - (NSInteger)roundCount;
23 |
24 | // 当前局名
25 | - (NSString *)currentRoundName;
26 |
27 | // 当前庄家
28 | - (NSString *)currentOYAName;
29 |
30 | // 当前OYAIndex
31 | - (NSUInteger)currentOYAIndex;
32 |
33 | // 玩家的名称
34 | - (NSArray *)playerNames;
35 |
36 | // 玩家的风位
37 | - (NSArray *)currentPlayerWinds;
38 |
39 | // 玩家的分数
40 | - (NSArray *)currentPlayerScores;
41 |
42 | // 某一局的名称
43 | - (NSString *)roundNameAtIndex:(NSUInteger)index;
44 |
45 | // 某一局的分数变化
46 | - (NSArray *)roundScoreChangesAtIndex:(NSUInteger)index;
47 |
48 | // 当前局的分数变化
49 | - (NSArray *)currentRoundScoreChanges;
50 |
51 | // 当前局中立直的人
52 | - (NSArray *)currentRichiPlayers;
53 |
54 | // 立直
55 | - (void)richiAtPlayers:(NSArray *)richiPlayerIndexes;
56 |
57 | // 当前的立直棒数
58 | - (NSInteger)currentRichiCount;
59 |
60 | // 自摸
61 | - (void)tsumoAtPlayer:(NSUInteger)playerIndex fan:(NSInteger)fan fu:(NSInteger)fu;
62 |
63 | // 放铳
64 | - (void)ronAtPlayers:(NSArray *)winners loser:(NSUInteger)loserIndex fan:(NSArray *)fan fu:(NSArray *)fu;
65 |
66 | // 流局
67 | - (void)drawForType:(NSInteger)type players:(NSArray *)players oyaTenpai:(BOOL)oyaTenpai;
68 |
69 | // 游戏是否结束
70 | - (BOOL)isGameEnded;
71 |
72 | // 玩家排位
73 | - (NSArray *)sortedPlayers;
74 |
75 | // 撤销上一局
76 | - (void)cancelLastRound;
77 |
78 | @end
79 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/MMSCGameManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCGameManager.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/2/11.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCGameManager.h"
10 | #import "MMSCGame.h"
11 | #import "MMSCPlayer.h"
12 | #import "MMSCTsuMoResult.h"
13 | #import "MMSCTwoWinnerResult.h"
14 | #import "MMSCNormalWinResult.h"
15 | #import "MMSCDrawManganResult.h"
16 |
17 | @interface MMSCGameManager ()
18 |
19 | @property (nonatomic, strong) MMSCGame *game;
20 |
21 | @end
22 |
23 | @implementation MMSCGameManager
24 |
25 | + (MMSCGameManager *)instance {
26 | static MMSCGameManager *instance = nil;
27 | static dispatch_once_t onceToken;
28 | dispatch_once(&onceToken, ^{
29 | instance = [[MMSCGameManager alloc] init];
30 | });
31 |
32 | return instance;
33 | }
34 |
35 | - (instancetype)init {
36 | if (self = [super init]) {
37 | _game = [[MMSCGame alloc] init];
38 | }
39 |
40 | return self;
41 | }
42 |
43 | - (void)initGameWithPlayers:(NSArray *)playerNames {
44 | self.game.players = [self createPlayersWithNames:playerNames];
45 |
46 | [self.game gameInit];
47 | }
48 |
49 | - (NSArray *)randomSeatWithPlayers:(NSArray *)playerNames {
50 |
51 | self.game.players = [self createPlayersWithNames:playerNames];
52 | [self.game randomSeat];
53 |
54 | return [self playerNames];
55 | }
56 |
57 | - (NSArray *)createPlayersWithNames:(NSArray *)playerNames {
58 | NSMutableArray *players = [NSMutableArray array];
59 |
60 | for (NSString *playerName in playerNames) {
61 | MMSCPlayer *player = [[MMSCPlayer alloc] initWithName:playerName];
62 | [players addObject:player];
63 | }
64 |
65 | return [players copy];
66 | }
67 |
68 | - (NSInteger)roundCount {
69 | return self.game.rounds.count;
70 | }
71 |
72 | - (NSString *)currentRoundName {
73 | MMSCRound *currentRound = [self.game currentRound];
74 | return [currentRound roundName];
75 | }
76 |
77 | - (NSString *)currentOYAName {
78 | MMSCPlayer *oya = [self.game getOYA];
79 | return oya.name;
80 | }
81 |
82 | - (NSUInteger)currentOYAIndex {
83 | return self.game.oyaIndex;
84 | }
85 |
86 | - (NSArray *)playerNames {
87 | NSMutableArray *playerNames = [NSMutableArray array];
88 | for (MMSCPlayer *player in self.game.players) {
89 | [playerNames addObject:player.name];
90 | }
91 |
92 | return [playerNames copy];
93 | }
94 |
95 | - (NSArray *)currentPlayerWinds {
96 | NSMutableArray *playerWinds = [NSMutableArray array];
97 | for (MMSCPlayer *player in self.game.players) {
98 | [playerWinds addObject:[self convertWindEnumToCharacter:player.wind]];
99 | }
100 |
101 | return playerWinds;
102 | }
103 |
104 | - (NSArray *)currentPlayerScores {
105 | NSMutableArray *playerScores = [NSMutableArray array];
106 | for (MMSCPlayer *player in self.game.players) {
107 | [playerScores addObject:@(player.score)];
108 | }
109 |
110 | return playerScores;
111 | }
112 |
113 | - (NSString *)convertWindEnumToCharacter:(MMSCWind)wind {
114 | switch (wind) {
115 | case MMSCWindEast:
116 | return @"東";
117 | case MMSCWindSouth:
118 | return @"南";
119 | case MMSCWindWest:
120 | return @"西";
121 | case MMSCWindNorth:
122 | return @"北";
123 | }
124 | }
125 |
126 | - (NSString *)roundNameAtIndex:(NSUInteger)index {
127 | NSAssert(index < self.game.rounds.count, @"round index out of bound");
128 |
129 | MMSCRound *round = self.game.rounds[index];
130 | return round.roundName;
131 | }
132 |
133 | - (NSArray *)roundScoreChangesAtIndex:(NSUInteger)index {
134 | NSAssert(index < self.game.rounds.count, @"round index out of bound");
135 |
136 | MMSCRound *round = self.game.rounds[index];
137 | return [round roundScoreChanges];
138 | }
139 |
140 | - (NSArray *)currentRoundScoreChanges {
141 | MMSCRound *round = [self.game currentRound];
142 | return [round roundScoreChanges];
143 | }
144 |
145 | - (NSArray *)currentRichiPlayers {
146 | MMSCRound *currentRound = [self.game currentRound];
147 | return currentRound.richiPlayerIndexes;
148 | }
149 |
150 | - (void)richiAtPlayers:(NSArray *)richiPlayerIndexes {
151 | NSMutableArray *currentRichiPlayers = [[self currentRichiPlayers] mutableCopy];
152 | for (NSNumber *playerIndex in richiPlayerIndexes) {
153 | if ([currentRichiPlayers containsObject:playerIndex]) { // 之前已经立直过了
154 | [currentRichiPlayers removeObject:playerIndex];
155 | } else {
156 | [self.game richiAtPlayerIndex:playerIndex.unsignedIntegerValue];
157 | }
158 | }
159 |
160 | if (currentRichiPlayers.count > 0) { // 有取消的情况发生
161 | for (NSNumber *playerIndex in currentRichiPlayers) {
162 | [self.game cancelRichiAtPlayerIndex:playerIndex.unsignedIntegerValue];
163 | }
164 | }
165 | }
166 |
167 | - (NSInteger)currentRichiCount {
168 | NSInteger currentRichiScore = [self.game currentRound].richiScore;
169 | return currentRichiScore / 1000;
170 | }
171 |
172 | - (void)tsumoAtPlayer:(NSUInteger)playerIndex fan:(NSInteger)fan fu:(NSInteger)fu {
173 |
174 | MMSCTsuMoResult *tsumoResult = [[MMSCTsuMoResult alloc] init];
175 | tsumoResult.winnerIndex = playerIndex;
176 | tsumoResult.fan = fan;
177 | tsumoResult.fu = fu;
178 |
179 | [self.game endCurrentRoundWithResult:tsumoResult];
180 | }
181 |
182 | - (void)ronAtPlayers:(NSArray *)winners loser:(NSUInteger)loserIndex fan:(NSArray *)fan fu:(NSArray *)fu {
183 | MMSCRoundResult *result = nil;
184 | if (winners.count > 1) {
185 | MMSCTwoWinnerResult *twoWinResult = [[MMSCTwoWinnerResult alloc] init];
186 | twoWinResult.winner1Index = [winners[0] unsignedIntegerValue];
187 | twoWinResult.winner2Index = [winners[1] unsignedIntegerValue];
188 |
189 | twoWinResult.fanOfWiiner1 = [fan[0] integerValue];
190 | twoWinResult.fanOfWinner2 = [fan[1] integerValue];
191 |
192 | twoWinResult.fuOfWinner1 = [fu[0] integerValue];
193 | twoWinResult.fuOfWinner2 = [fu[1] integerValue];
194 |
195 | twoWinResult.payerIndex = loserIndex;
196 | result = twoWinResult;
197 | } else {
198 | MMSCNormalWinResult *normalResult = [[MMSCNormalWinResult alloc] init];
199 | normalResult.winnerIndex = [winners[0] unsignedIntegerValue];
200 | normalResult.fan = [fan[0] integerValue];
201 | normalResult.fu = [fu[0] integerValue];
202 |
203 | normalResult.payerIndex = loserIndex;
204 | result= normalResult;
205 | }
206 |
207 | [self.game endCurrentRoundWithResult:result];
208 | }
209 |
210 | - (void)drawForType:(NSInteger)type players:(NSArray *)players oyaTenpai:(BOOL)oyaTenpai {
211 | MMSCRoundResult *result = nil;
212 | if (type == MMSCDrawType_DrawMangan) {
213 | MMSCDrawManganResult *drawMangan = [[MMSCDrawManganResult alloc] init];
214 | drawMangan.type = type;
215 | drawMangan.manganPlayerIndexes = players;
216 | drawMangan.oyaTenpai = oyaTenpai;
217 | result = drawMangan;
218 | } else {
219 | MMSCDrawResult *drawResult = [[MMSCDrawResult alloc] init];
220 | drawResult.type = type;
221 | drawResult.tenpaiPlayerIndexes = players;
222 | result = drawResult;
223 | }
224 |
225 | [self.game endCurrentRoundWithResult:result];
226 | }
227 |
228 | - (BOOL)isGameEnded {
229 | return [self.game isGameEnded];
230 | }
231 |
232 | - (NSArray *)sortedPlayers {
233 | NSArray *sortedPlayers = [self.game playerRank];
234 |
235 | NSMutableArray *playerNames = [NSMutableArray array];
236 | for (MMSCPlayer *player in sortedPlayers) {
237 | [playerNames addObject:player.name];
238 | }
239 |
240 | return [playerNames copy];
241 | }
242 |
243 | - (void)cancelLastRound {
244 | [self.game cancelLastRound];
245 | }
246 |
247 | @end
248 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/MMSCPlayer.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCPlayer.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MMSCUtil.h"
11 |
12 | @interface MMSCPlayer : NSObject
13 |
14 | // 名字
15 | @property(nonatomic, strong) NSString *name;
16 | // 风位
17 | @property(nonatomic, assign) MMSCWind wind;
18 | // 分数
19 | @property(nonatomic, assign) NSInteger score;
20 |
21 | // 初始化
22 | - (instancetype)initWithName:(NSString *)playerName;
23 |
24 | // 加分
25 | - (void)increaseScore:(NSInteger)increment;
26 |
27 | // 扣分
28 | - (void)decreaseScore:(NSInteger)decrement;
29 |
30 | // 是不是庄家
31 | - (BOOL)isOYA;
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/MMSCPlayer.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCPlayer.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCPlayer.h"
10 |
11 | @implementation MMSCPlayer
12 |
13 | - (instancetype)initWithName:(NSString *)playerName {
14 | if (self = [self init]) {
15 | _name = playerName;
16 | _score = 25000;
17 | }
18 | return self;
19 | }
20 |
21 | - (void)increaseScore:(NSInteger)increment {
22 | self.score += increment;
23 | }
24 |
25 | - (void)decreaseScore:(NSInteger)decrement {
26 | self.score -= decrement;
27 | }
28 |
29 | - (BOOL)isOYA {
30 | return self.wind == MMSCWindEast;
31 | }
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/MMSCRound.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCRound.h
3 | // MahjongScoreCalculator
4 | //
5 | // 一场游戏中的一局
6 | //
7 | // Created by fredfx on 16/1/2.
8 | // Copyright © 2016年 fredfx. All rights reserved.
9 | //
10 |
11 | #import
12 | #import "MMSCUtil.h"
13 |
14 | @class MMSCRoundResult;
15 | @class MMSCPlayer;
16 |
17 | @interface MMSCRound : NSObject
18 |
19 | // 场风
20 | @property(nonatomic, assign) MMSCWind roundWind;
21 |
22 | // 局数(1,2,3,4)
23 | @property(nonatomic, assign) NSInteger roundNumeber;
24 |
25 | // 本场数
26 | @property(nonatomic, assign) NSInteger bonbanNumber;
27 |
28 | // 结果
29 | @property(nonatomic, strong) MMSCRoundResult *result;
30 |
31 | // 场上的立直棒
32 | @property(nonatomic, assign) NSInteger richiScore;
33 |
34 | // 本场立直的玩家
35 | @property(nonatomic, strong) NSMutableArray *richiPlayerIndexes;
36 |
37 | // 换庄
38 | @property(nonatomic, assign) BOOL oyaChanged;
39 |
40 | // 立直
41 | - (void)richiAtPlayer:(MMSCPlayer *)player index:(NSUInteger)index;
42 |
43 | // 取消立直
44 | - (void)cancelRichiAtPlayer:(MMSCPlayer *)player index:(NSUInteger)index;
45 |
46 | // 结束该局
47 | - (void)endRoundWithPlayers:(NSArray *)players;
48 |
49 | // 该局的分数变化
50 | - (NSArray *)roundScoreChanges;
51 |
52 | // 此局名称
53 | - (NSString *)roundName;
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/MMSCRound.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCRound.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCRound.h"
10 | #import "MMSCPlayer.h"
11 | #import "MMSCRoundResult.h"
12 |
13 | @implementation MMSCRound
14 |
15 | - (instancetype)init {
16 | if (self = [super init]) {
17 | _richiPlayerIndexes = [NSMutableArray array];
18 | }
19 | return self;
20 | }
21 |
22 | - (void)richiAtPlayer:(MMSCPlayer *)player index:(NSUInteger)index {
23 | self.richiScore += 1000;
24 | [self.richiPlayerIndexes addObject:@(index)];
25 |
26 | [player decreaseScore:1000];
27 | }
28 |
29 | - (void)cancelRichiAtPlayer:(MMSCPlayer *)player index:(NSUInteger)index {
30 | NSNumber *indexNum = @(index);
31 | if (![self.richiPlayerIndexes containsObject:indexNum]) {
32 | // 没有立直过的不处理
33 | return;
34 | }
35 |
36 | self.richiScore -= 1000;
37 | [self.richiPlayerIndexes removeObject:indexNum];
38 |
39 | [player increaseScore:1000];
40 | }
41 |
42 | - (void)endRoundWithPlayers:(NSArray *)players {
43 | if (!self.result) {
44 | NSAssert(NO, @"未设置本局结果");
45 | }
46 |
47 | [self.result recordRichiChangeWithRichiPlayers:self.richiPlayerIndexes];
48 | [self.result assignScoreWithPlayers:players bonban:self.bonbanNumber richiScore:self.richiScore];
49 | }
50 |
51 | - (NSString *)roundName {
52 | NSString *wind = [MMSCUtil convertWindEnumToCharacter:self.roundWind];
53 | NSString *rounNumber = [MMSCUtil convertToCharacterWithNumber:self.roundNumeber];
54 |
55 | if (self.bonbanNumber <= 0) {
56 | return [NSString stringWithFormat:@"%@%@局", wind, rounNumber];
57 | }
58 |
59 | return [NSString stringWithFormat:@"%@%@局 %zd本场", wind, rounNumber, self.bonbanNumber];
60 | }
61 |
62 | - (NSArray *)roundScoreChanges {
63 | if (self.result) { // 已经设置了结果并结算完了就直接返回
64 | return [self.result.scoreChanges copy];
65 | }
66 |
67 | NSMutableArray *scoreChanges = [NSMutableArray arrayWithArray:@[@(0), @(0), @(0), @(0)]];
68 | for (NSNumber *index in self.richiPlayerIndexes) {
69 | scoreChanges[index.unsignedIntegerValue] = @(-1000);
70 | }
71 |
72 | return [scoreChanges copy];
73 | }
74 |
75 | @end
76 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/MMSCUtil.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCUtil.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef NS_ENUM(NSInteger, MMSCWind) {
12 | MMSCWindEast = 0,
13 | MMSCWindSouth,
14 | MMSCWindWest,
15 | MMSCWindNorth
16 | };
17 |
18 | #define PLAYER_NUMBER 4
19 |
20 | @interface MMSCUtil : NSObject
21 |
22 | // 风名
23 | + (NSString *)convertWindEnumToCharacter:(MMSCWind)wind;
24 |
25 | // 转换为中文
26 | + (NSString *)convertToCharacterWithNumber:(NSInteger)number;
27 |
28 | // 取到100
29 | + (NSInteger)increaseToHundredForScore:(NSInteger)score;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/MMSCUtil.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCUtil.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCUtil.h"
10 |
11 | @implementation MMSCUtil
12 |
13 | + (NSString *)convertWindEnumToCharacter:(MMSCWind)wind {
14 | switch (wind) {
15 | case MMSCWindEast:
16 | return @"东";
17 | case MMSCWindSouth:
18 | return @"南";
19 | case MMSCWindWest:
20 | return @"西";
21 | case MMSCWindNorth:
22 | return @"北";
23 | }
24 | }
25 |
26 | + (NSString *)convertToCharacterWithNumber:(NSInteger)number {
27 |
28 | static NSArray *unitCharacter;
29 | static dispatch_once_t unitOnce;
30 | dispatch_once(&unitOnce, ^{
31 | unitCharacter = @[@"一",@"二",@"三",@"四",@"五",@"六",@"七",@"八",@"九"];
32 | });
33 |
34 | static NSArray *tenAndHundredCharacter;
35 | static dispatch_once_t tenAndHundredOnce;
36 | dispatch_once(&tenAndHundredOnce, ^{
37 | tenAndHundredCharacter = @[@"十",@"百",@"千",@"万"];
38 | });
39 |
40 | NSMutableString *result = [[NSMutableString alloc] init];
41 |
42 | NSInteger tensIndex = 0;
43 | NSInteger unit = number % 10;
44 | NSInteger temp = number;
45 |
46 | while (temp != 0) {
47 | temp = temp / 10;
48 |
49 | if (unit != 0) {
50 |
51 | //0-9
52 | if (!(tensIndex == 1 && unit == 1 && temp == 0)) {
53 | [result insertString:unitCharacter[unit - 1] atIndex:0];
54 | }
55 |
56 | unit = temp % 10;
57 |
58 | if (temp != 0 && unit != 0) {
59 | [result insertString:tenAndHundredCharacter[tensIndex] atIndex:0];
60 | }
61 |
62 | tensIndex ++;
63 | }
64 | }
65 |
66 | return result;
67 | }
68 |
69 | + (NSInteger)increaseToHundredForScore:(NSInteger)score {
70 | NSInteger temp = score % 100;
71 |
72 | if (temp != 0) {
73 | score = score - temp + 100;
74 | }
75 |
76 | return score;
77 | }
78 |
79 | @end
80 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCDrawManganResult.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCDrawManganResult.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/3.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCRoundResult.h"
10 | #import "MMSCDrawResult.h"
11 |
12 | @interface MMSCDrawManganResult : MMSCDrawResult
13 |
14 | @property(nonatomic, strong) NSArray *manganPlayerIndexes;
15 |
16 | @property(nonatomic, assign) BOOL oyaTenpai;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCDrawManganResult.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCDrawManganResult.m
3 | // MahjongScoreCalculator
4 | //
5 | // 流局满贯
6 | //
7 | // Created by fredfx on 16/1/3.
8 | // Copyright © 2016年 fredfx. All rights reserved.
9 | //
10 |
11 | #import "MMSCDrawManganResult.h"
12 |
13 | @implementation MMSCDrawManganResult
14 |
15 | - (instancetype)init {
16 | if (self = [super init]) {
17 | self.type = MMSCDrawType_DrawMangan;
18 | }
19 |
20 | return self;
21 | }
22 |
23 | - (void)assignScoreWithPlayers:(NSArray *)players bonban:(NSInteger)bonban richiScore:(NSInteger)richiScore {
24 |
25 |
26 | for (int i = 0; i < _manganPlayerIndexes.count; i++) {
27 |
28 | NSInteger playerIndex = [_manganPlayerIndexes[i] integerValue];
29 | MMSCPlayer *winner = (MMSCPlayer *)players[playerIndex];
30 |
31 | if ([winner isOYA]) {
32 | [self recordScoreChangeAtIndex:playerIndex player:winner change:12000];
33 | [players enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
34 | MMSCPlayer *player = (MMSCPlayer *)obj;
35 | if (player != winner) {
36 | [self recordScoreChangeAtIndex:idx player:player change:-4000];
37 | }
38 | }];
39 | } else {
40 | [self recordScoreChangeAtIndex:playerIndex player:winner change:8000];
41 | [players enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
42 | MMSCPlayer *player = (MMSCPlayer *)obj;
43 | if (player != winner) {
44 | NSInteger decresement = [player isOYA] ? 4000 : 2000;
45 | [self recordScoreChangeAtIndex:idx player:player change:-1 * decresement];
46 | }
47 | }];
48 | }
49 | }
50 | }
51 |
52 | - (BOOL)changeOYA:(NSUInteger)currentOYAIndex {
53 | return !self.oyaTenpai;
54 | }
55 |
56 | @end
57 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCDrawResult.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCDrawResult.h
3 | // MahjongScoreCalculator
4 | //
5 | // 流局
6 | //
7 | // Created by fredfx on 16/1/3.
8 | // Copyright © 2016年 fredfx. All rights reserved.
9 | //
10 |
11 | #import "MMSCRoundResult.h"
12 |
13 | typedef NS_ENUM(NSInteger, MMSCDrawType) {
14 | MMSCDrawType_Normal, // 普通流局
15 | MMSCDrawType_NineHonor, // 九种九牌
16 | MMSCDrawType_FourWind, // 四风连打
17 | MMSCDrawType_FourKan, // 四杠流局
18 | MMSCDrawType_FourRichi, // 四家立直
19 | MMSCDrawType_ThreeWin, // 一炮三响
20 | MMSCDrawType_DrawMangan, // 流局满贯
21 | };
22 |
23 | @interface MMSCDrawResult : MMSCRoundResult
24 |
25 | // 流局的类型
26 | @property(nonatomic, assign) MMSCDrawType type;
27 |
28 | // 听牌的人
29 | @property(nonatomic, strong) NSArray *tenpaiPlayerIndexes;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCDrawResult.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCDrawResult.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/3.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCDrawResult.h"
10 | #import "MMSCUtil.h"
11 |
12 | @implementation MMSCDrawResult
13 |
14 | - (void)assignScoreWithPlayers:(NSArray *)players bonban:(NSInteger)bonban richiScore:(NSInteger)richiScore {
15 |
16 | if (_type != MMSCDrawType_Normal) {
17 | return;
18 | }
19 |
20 | if (_tenpaiPlayerIndexes.count == PLAYER_NUMBER || _tenpaiPlayerIndexes.count == 0) {
21 | return;
22 | }
23 |
24 | NSInteger incresement = 3000 / _tenpaiPlayerIndexes.count;
25 | NSInteger decresement = 3000 / (PLAYER_NUMBER - _tenpaiPlayerIndexes.count);
26 |
27 | [players enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
28 | MMSCPlayer *player = (MMSCPlayer *)obj;
29 |
30 | if ([_tenpaiPlayerIndexes containsObject:@(idx)]) {
31 | [self recordScoreChangeAtIndex:idx player:player change:incresement];
32 | } else {
33 | [self recordScoreChangeAtIndex:idx player:player change:-1 * decresement];
34 | }
35 | }];
36 | }
37 |
38 | - (BOOL)isDraw {
39 | return YES;
40 | }
41 |
42 | - (BOOL)changeOYA:(NSUInteger)currentOYAIndex {
43 | switch (self.type) {
44 | case MMSCDrawType_FourWind:
45 | return YES;
46 | case MMSCDrawType_DrawMangan:
47 | case MMSCDrawType_Normal:
48 | return ![self.tenpaiPlayerIndexes containsObject:@(currentOYAIndex)];
49 | default:
50 | return NO;
51 | }
52 | }
53 |
54 | @end
55 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCNormalWinResult.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCNormalWinResult.h
3 | // MahjongScoreCalculator
4 | //
5 | // 普通的点炮
6 | //
7 | // Created by fredfx on 16/1/3.
8 | // Copyright © 2016年 fredfx. All rights reserved.
9 | //
10 |
11 | #import "MMSCRoundResult.h"
12 |
13 | @interface MMSCNormalWinResult : MMSCRoundResult
14 |
15 | // 胡了的人
16 | @property(nonatomic, assign) NSUInteger winnerIndex;
17 | // 番
18 | @property(nonatomic, assign) NSInteger fan;
19 | // 符
20 | @property(nonatomic, assign) NSInteger fu;
21 |
22 | // 点炮的
23 | @property(nonatomic, assign) NSUInteger payerIndex;
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCNormalWinResult.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCNormalWinResult.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/3.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCNormalWinResult.h"
10 |
11 | @implementation MMSCNormalWinResult
12 |
13 | - (void)assignScoreWithPlayers:(NSArray *)players bonban:(NSInteger)bonban richiScore:(NSInteger)richiScore {
14 |
15 | MMSCPlayer *winner = players[_winnerIndex];
16 | MMSCPlayer *payer = players[_payerIndex];
17 |
18 | NSInteger basicScore = [self calculateScoreAtFan:_fan fu:_fu isOYAWin:[winner isOYA]];
19 |
20 | [self recordScoreChangeAtIndex:_winnerIndex player:winner change:basicScore + (300 * bonban) + richiScore];
21 |
22 | [self recordScoreChangeAtIndex:_payerIndex player:payer change:-1 * (basicScore + 300 * bonban)];
23 | }
24 |
25 | - (BOOL)changeOYA:(NSUInteger)currentOYAIndex {
26 | return currentOYAIndex != self.winnerIndex;
27 | }
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCRoundResult.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCRoundResult.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MMSCPlayer.h"
11 |
12 | @interface MMSCRoundResult : NSObject
13 |
14 | // 分数的变化情况
15 | @property(nonatomic, strong) NSMutableArray *scoreChanges;
16 |
17 | // 是否流局
18 | - (BOOL)isDraw;
19 |
20 | // 是否换庄
21 | - (BOOL)changeOYA:(NSUInteger)currentOYAIndex;
22 |
23 | // 分配分数
24 | - (void)assignScoreWithPlayers:(NSArray *)players bonban:(NSInteger)bonban richiScore:(NSInteger)richiScore;
25 |
26 | // 记录分数变化
27 | - (void)recordScoreChangeAtIndex:(NSUInteger)index player:(MMSCPlayer *)player change:(NSInteger)change;
28 |
29 | // 记录立直棒
30 | - (void)recordRichiChangeWithRichiPlayers:(NSArray *)playerIdxs;
31 |
32 | // 计算分数
33 | - (NSInteger)calculateScoreAtFan:(NSInteger)fan fu:(NSInteger)fu isOYAWin:(BOOL)isOYAWin;
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCRoundResult.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCRoundResult.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCRoundResult.h"
10 |
11 | @implementation MMSCRoundResult
12 |
13 | - (BOOL)isDraw {
14 | return NO;
15 | }
16 |
17 | - (BOOL)changeOYA:(NSUInteger)currentOYAIndex {
18 | return YES;
19 | }
20 |
21 | - (void)assignScoreWithPlayers:(NSArray *)players bonban:(NSInteger)bonban richiScore:(NSInteger)richiScore {
22 | NSAssert(NO, @"父类方法不该被直接调用");
23 | }
24 |
25 | - (void)recordScoreChangeAtIndex:(NSUInteger)index player:(MMSCPlayer *)player change:(NSInteger)change {
26 | NSInteger currentChange = [self.scoreChanges[index] integerValue];
27 |
28 | currentChange += change;
29 |
30 | self.scoreChanges[index] = @(currentChange);
31 |
32 | if (change > 0) {
33 | [player increaseScore:change];
34 | } else {
35 | [player decreaseScore:-1 * change];
36 | }
37 | }
38 |
39 | - (void)recordRichiChangeWithRichiPlayers:(NSArray *)playerIdxs {
40 | for (NSNumber *idxNum in playerIdxs) {
41 | NSUInteger idx = idxNum.unsignedIntegerValue;
42 | NSInteger currentChange = [self.scoreChanges[idx] integerValue];
43 |
44 | currentChange -= 1000;
45 |
46 | self.scoreChanges[idx] = @(currentChange);
47 | }
48 | }
49 |
50 | - (instancetype)init {
51 | if (self = [super init]) {
52 | _scoreChanges = [NSMutableArray arrayWithArray:@[@(0), @(0), @(0), @(0)]];
53 | }
54 | return self;
55 | }
56 |
57 | - (NSInteger)calculateScoreAtFan:(NSInteger)fan fu:(NSInteger)fu isOYAWin:(BOOL)isOYAWin {
58 |
59 | NSInteger score;
60 |
61 | if (fan >= 5 || (fan == 4 && fu >= 40) || (fan == 3 && fu >= 70)) {
62 | score = [self calculateManganAtFan:fan isOYAWin:isOYAWin];
63 | } else {
64 | score = [self calculateNormalAtFan:fan fu:fu isOYAWin:isOYAWin];
65 | }
66 |
67 | return score;
68 | }
69 |
70 | - (NSInteger)calculateManganAtFan:(NSInteger)fan isOYAWin:(BOOL)isOYAWin {
71 |
72 | NSAssert(fan >= 3, @"小于3番做什么满贯");
73 |
74 | NSInteger result;
75 |
76 | switch (fan) {
77 | case 3:
78 | case 4:
79 | case 5:
80 | result = 8000;
81 | break;
82 | case 6:
83 | case 7:
84 | result = 12000;
85 | break;
86 | case 8:
87 | case 9:
88 | case 10:
89 | result = 16000;
90 | break;
91 | case 11:
92 | case 12:
93 | result = 24000;
94 | break;
95 | default:
96 | result = 32000;
97 | break;
98 | }
99 |
100 | if (isOYAWin) {
101 | result *= 1.5;
102 | }
103 |
104 | return result;
105 | }
106 |
107 | - (NSInteger)calculateNormalAtFan:(NSInteger)fan fu:(NSInteger)fu isOYAWin:(BOOL)isOYAWin {
108 |
109 | NSInteger basic = fu * (NSInteger)pow(2, fan + 2);
110 |
111 | if (isOYAWin) {
112 | return [MMSCUtil increaseToHundredForScore:basic * 6];
113 | } else {
114 | return [MMSCUtil increaseToHundredForScore:basic * 4];
115 | }
116 | }
117 |
118 | @end
119 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCTsuMoResult.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCTsuMoResult.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/3.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCRoundResult.h"
10 |
11 | @interface MMSCTsuMoResult : MMSCRoundResult
12 |
13 | // 自摸的玩家的索引
14 | @property(nonatomic, assign) NSUInteger winnerIndex;
15 |
16 | // 番
17 | @property(nonatomic, assign) NSInteger fan;
18 |
19 | // 符
20 | @property(nonatomic, assign) NSInteger fu;
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCTsuMoResult.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCTsuMoResult.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/3.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCTsuMoResult.h"
10 | #import "MMSCUtil.h"
11 |
12 | @implementation MMSCTsuMoResult
13 |
14 | - (void)assignScoreWithPlayers:(NSArray *)players bonban:(NSInteger)bonban richiScore:(NSInteger)richiScore {
15 |
16 | MMSCPlayer *winner = players[_winnerIndex];
17 |
18 | NSInteger basicScore = [self calculateScoreAtFan:_fan fu:_fu isOYAWin:[winner isOYA]];
19 |
20 | //立直棒
21 | [self recordScoreChangeAtIndex:_winnerIndex player:winner change:richiScore];
22 |
23 | if ([winner isOYA]) {
24 | // OYA自摸
25 | NSInteger payment = [MMSCUtil increaseToHundredForScore:(basicScore / 3) + (100 * bonban)];
26 | [self recordScoreChangeAtIndex:_winnerIndex player:winner change:payment * 3];
27 |
28 | [players enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
29 | MMSCPlayer *player = (MMSCPlayer *)obj;
30 |
31 | if (player != winner) {
32 | [self recordScoreChangeAtIndex:idx player:player change:-1 * payment];
33 | }
34 | }];
35 | } else {
36 | // 炸庄
37 | NSInteger oyaPayment = [MMSCUtil increaseToHundredForScore:(basicScore / 2)] + (100 * bonban);
38 | NSInteger normalPayment = [MMSCUtil increaseToHundredForScore:(basicScore / 4)] + (100 * bonban);
39 |
40 | [self recordScoreChangeAtIndex:_winnerIndex player:winner change:oyaPayment + 2 * normalPayment];
41 |
42 | [players enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
43 | MMSCPlayer *player = (MMSCPlayer *)obj;
44 |
45 | if (player != winner) {
46 | NSInteger change = -1 * ([player isOYA] ? oyaPayment : normalPayment);
47 | [self recordScoreChangeAtIndex:idx player:player change:change];
48 | }
49 | }];
50 | }
51 | }
52 |
53 | - (BOOL)changeOYA:(NSUInteger)currentOYAIndex {
54 | return currentOYAIndex != self.winnerIndex;
55 | }
56 |
57 | @end
58 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCTwoWinnerResult.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCTwoWinnerResult.h
3 | // MahjongScoreCalculator
4 | //
5 | // 一炮双响
6 | //
7 | // Created by fredfx on 16/1/3.
8 | // Copyright © 2016年 fredfx. All rights reserved.
9 | //
10 |
11 | #import "MMSCRoundResult.h"
12 |
13 | @interface MMSCTwoWinnerResult : MMSCRoundResult
14 |
15 | // 第一个
16 | @property(nonatomic, assign) NSInteger winner1Index;
17 | @property(nonatomic, assign) NSInteger fanOfWiiner1;
18 | @property(nonatomic, assign) NSInteger fuOfWinner1;
19 |
20 | // 第二个
21 | @property(nonatomic, assign) NSInteger winner2Index;
22 | @property(nonatomic, assign) NSInteger fanOfWinner2;
23 | @property(nonatomic, assign) NSInteger fuOfWinner2;
24 |
25 | // 点炮的
26 | @property(nonatomic, assign) NSInteger payerIndex;
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/GameCore/RoundResult/MMSCTwoWinnerResult.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCTwoWinnerResult.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/3.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCTwoWinnerResult.h"
10 |
11 | @implementation MMSCTwoWinnerResult
12 |
13 | - (void)assignScoreWithPlayers:(NSArray *)players bonban:(NSInteger)bonban richiScore:(NSInteger)richiScore {
14 |
15 | // 立直棒和本场分数
16 | NSUInteger extraScoreIndex = [self getBonbanAndRichiScorePlayerIndex];
17 | MMSCPlayer *extraScorePlayer = players[extraScoreIndex];
18 |
19 | [self recordScoreChangeAtIndex:extraScoreIndex player:extraScorePlayer change:richiScore + (300 * bonban)];
20 |
21 | MMSCPlayer *winner1 = players[_winner1Index];
22 | MMSCPlayer *winner2 = players[_winner2Index];
23 | MMSCPlayer *payer = players[_payerIndex];
24 |
25 | NSInteger basicScore1 = [self calculateScoreAtFan:_fanOfWiiner1 fu:_fuOfWinner1 isOYAWin:[winner1 isOYA]];
26 | [self recordScoreChangeAtIndex:_winner1Index player:winner1 change:basicScore1];
27 |
28 | NSInteger basicScore2 = [self calculateScoreAtFan:_fanOfWinner2 fu:_fuOfWinner2 isOYAWin:[winner2 isOYA]];
29 | [self recordScoreChangeAtIndex:_winner2Index player:winner2 change:basicScore2];
30 |
31 | NSInteger payment = basicScore1 + basicScore2 + 300 * bonban;
32 | [self recordScoreChangeAtIndex:_payerIndex player:payer change:-1 * payment];
33 | }
34 |
35 | // 计算拿点棒和本场分数的人:点炮者的下家->对家->上家
36 | - (NSUInteger)getBonbanAndRichiScorePlayerIndex {
37 |
38 | NSInteger distance1 = _winner1Index - _payerIndex;
39 | NSInteger distance2 = _winner2Index - _payerIndex;
40 |
41 | if (distance1 * distance2 < 0) { // 一个是点炮的下家,另一个是上家
42 | return distance1 > 0 ? _winner1Index : _winner2Index;
43 | }
44 |
45 | return distance1 < distance2 ? _winner1Index : _winner2Index; //总之选距离点炮者比较近的
46 | }
47 |
48 | - (BOOL)changeOYA:(NSUInteger)currentOYAIndex {
49 | return currentOYAIndex != self.winner1Index && currentOYAIndex != self.winner2Index;
50 | }
51 |
52 | @end
53 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIcons
10 |
11 | CFBundleIcons~ipad
12 |
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | 1.0
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | 1
27 | LSRequiresIPhoneOS
28 |
29 | UILaunchStoryboardName
30 | LaunchScreen
31 | UIMainStoryboardFile
32 | Main
33 | UIRequiredDeviceCapabilities
34 |
35 | armv7
36 |
37 | UISupportedInterfaceOrientations
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationLandscapeLeft
41 | UIInterfaceOrientationLandscapeRight
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCFontAndColorUtil.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCFontAndColorUtil.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/30.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | typedef NS_ENUM(NSInteger, MMSCPlayerViewTag) {
13 | MMSCPlayerViewTagPlayer1 = 0x666,
14 | MMSCPlayerViewTagPlayer2,
15 | MMSCPlayerViewTagPlayer3,
16 | MMSCPlayerViewTagPlayer4,
17 | };
18 |
19 | @interface MMSCFontAndColorUtil : NSObject
20 |
21 | // 欢迎页顶层字体
22 | + (UIFont *)titleSummaryFont;
23 |
24 | // 大号风向字体
25 | + (UIFont *)windBigLabelFont;
26 |
27 | // 欢迎页普通字体
28 | + (UIFont *)startpageFont;
29 |
30 | // 欢迎页背景颜色
31 | + (UIColor *)startPageBackgroundColor;
32 |
33 | // 欢迎页Label字体颜色
34 | + (UIColor *)startPageLabelFontColor;
35 |
36 | // Color2Image
37 | + (UIImage *)imageWithColor:(UIColor *)color;
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCFontAndColorUtil.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCFontAndColorUtil.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/30.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCFontAndColorUtil.h"
10 |
11 | @implementation MMSCFontAndColorUtil
12 |
13 | #pragma mark ---------------Font----------------
14 |
15 | // TODO 重构成KeyValue对
16 |
17 | + (UIFont *)titleSummaryFont {
18 | static UIFont *titleSummaryFont = nil;
19 | static dispatch_once_t titleOnce;
20 | dispatch_once(&titleOnce, ^{
21 | titleSummaryFont = [UIFont systemFontOfSize:30];
22 | });
23 |
24 | return titleSummaryFont;
25 | }
26 |
27 | + (UIFont *)windBigLabelFont {
28 | static UIFont *windBigFont = nil;
29 | static dispatch_once_t windBigOnce;
30 | dispatch_once(&windBigOnce, ^{
31 | windBigFont = [UIFont systemFontOfSize:50 weight:5];
32 | });
33 | return windBigFont;
34 | }
35 |
36 | + (UIFont *)startpageFont {
37 | static UIFont *playerNameFont = nil;
38 | static dispatch_once_t playerNameOnce;
39 | dispatch_once(&playerNameOnce, ^{
40 | playerNameFont = [UIFont systemFontOfSize:25];
41 | });
42 | return playerNameFont;
43 | }
44 |
45 | #pragma mark ---------------Color-----------------
46 |
47 | + (UIColor *)startPageBackgroundColor {
48 | static UIColor *startPageBGColor = nil;
49 | static dispatch_once_t startBGCOnce;
50 | dispatch_once(&startBGCOnce, ^{
51 | startPageBGColor = [[UIColor alloc] initWithRed:99/255.f green:133/255.f blue:167/255.f alpha:1];
52 | });
53 | return startPageBGColor;
54 | }
55 |
56 | + (UIColor *)startPageLabelFontColor {
57 | static UIColor *spLabelFC = nil;
58 | static dispatch_once_t spLabelOnce;
59 | dispatch_once(&spLabelOnce, ^{
60 | spLabelFC = [[UIColor alloc] initWithRed:217/255.f green:208/255.f blue:199/255.f alpha:1];
61 | });
62 | return spLabelFC;
63 | }
64 |
65 | + (UIImage *)imageWithColor:(UIColor *)color {
66 | CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
67 | UIGraphicsBeginImageContext(rect.size);
68 | CGContextRef context = UIGraphicsGetCurrentContext();
69 |
70 | CGContextSetFillColorWithColor(context, [color CGColor]);
71 | CGContextFillRect(context, rect);
72 |
73 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
74 | UIGraphicsEndImageContext();
75 |
76 | UIImage *resizeImage = [image resizableImageWithCapInsets:UIEdgeInsetsMake(0.5f, 0.5f, 0.5f, 0.5f)];
77 |
78 | return resizeImage;
79 | }
80 |
81 | @end
82 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCPlayerNameAndSeatController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCPlayerNameAndSeatController.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/30.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MMSCPlayerNameAndSeatController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCPlayerNameAndSeatController.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCPlayerNameAndSeatController.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/30.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCPlayerNameAndSeatController.h"
10 | #import "MMSCFontAndColorUtil.h"
11 | #import "MMSCRandomSeatTipsController.h"
12 | #import "MMSCScoreTableController.h"
13 | #import "MMSCGameManager.h"
14 |
15 | @interface MMSCPlayerNameAndSeatController ()
16 |
17 | @property (nonatomic, strong) NSArray *playerNames;
18 |
19 | @end
20 |
21 | @implementation MMSCPlayerNameAndSeatController
22 |
23 | - (void)viewDidLoad {
24 | [super viewDidLoad];
25 |
26 | [self.view setBackgroundColor:[MMSCFontAndColorUtil startPageBackgroundColor]];
27 |
28 | CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
29 |
30 | UILabel *summaryLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 30, screenWidth, 60)];
31 | [summaryLabel setFont:[MMSCFontAndColorUtil titleSummaryFont]];
32 | [summaryLabel setTextAlignment:NSTextAlignmentCenter];
33 | [summaryLabel setText:@"打麻将真TM开心"];
34 |
35 | [self.view addSubview:summaryLabel];
36 |
37 | CGFloat windLabelCenterX = 80;
38 | CGFloat windLabelCenterY = 165;
39 | CGFloat windLabelInterval = 80;
40 | CGFloat textFieldCenterX = windLabelCenterX + 126;
41 | [self generateLabelWind:@"東" center:CGPointMake(windLabelCenterX, windLabelCenterY)];
42 | UITextField *player1Field = [self generatePlayerNameFieldAtPoint:CGPointMake(textFieldCenterX, windLabelCenterY) tintText:@"天和"];
43 | player1Field.tag = MMSCPlayerViewTagPlayer1;
44 | [self.view addSubview:player1Field];
45 |
46 | windLabelCenterY += windLabelInterval;
47 | [self generateLabelWind:@"南" center:CGPointMake(windLabelCenterX, windLabelCenterY)];
48 | UITextField *player2Field = [self generatePlayerNameFieldAtPoint:CGPointMake(textFieldCenterX, windLabelCenterY) tintText:@"地和"];
49 | player2Field.tag = MMSCPlayerViewTagPlayer2;
50 | [self.view addSubview:player2Field];
51 |
52 | windLabelCenterY += windLabelInterval;
53 | [self generateLabelWind:@"西" center:CGPointMake(windLabelCenterX, windLabelCenterY)];
54 | UITextField *player3Field = [self generatePlayerNameFieldAtPoint:CGPointMake(textFieldCenterX, windLabelCenterY) tintText:@"人和"];
55 | player3Field.tag = MMSCPlayerViewTagPlayer3;
56 | [self.view addSubview:player3Field];
57 |
58 | windLabelCenterY += windLabelInterval;
59 | [self generateLabelWind:@"北" center:CGPointMake(windLabelCenterX, windLabelCenterY)];
60 | UITextField *player4Field = [self generatePlayerNameFieldAtPoint:CGPointMake(textFieldCenterX, windLabelCenterY) tintText:@"炸和"];
61 | player4Field.tag = MMSCPlayerViewTagPlayer4;
62 | [self.view addSubview:player4Field];
63 |
64 | UIButton * randomSeatButton = [self generateStartPageButtonAtCenter:CGPointMake(screenWidth / 2, windLabelCenterY + 80) text:@"随机座位"];
65 | [randomSeatButton addTarget:self action:@selector(waitForRandomSeat) forControlEvents:UIControlEventTouchUpInside];
66 | [self.view addSubview:randomSeatButton];
67 |
68 | UIButton * startBattleButton = [self generateStartPageButtonAtCenter:CGPointMake(screenWidth / 2, randomSeatButton.center.y + 60) text:@"赌上性命开始对日"];
69 | [startBattleButton addTarget:self action:@selector(startBattle:) forControlEvents:UIControlEventTouchUpInside];
70 | [self.view addSubview:startBattleButton];
71 | }
72 |
73 | - (void)generateLabelWind:(NSString *)wind center:(CGPoint)center {
74 | UILabel *windLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
75 | [windLabel setFont:[MMSCFontAndColorUtil windBigLabelFont]];
76 | [windLabel setTextColor:[MMSCFontAndColorUtil startPageLabelFontColor]];
77 | [windLabel setText:wind];
78 | [windLabel sizeToFit];
79 | windLabel.center = center;
80 |
81 | CGFloat seperaterCenterY = center.y + windLabel.frame.size.height / 2;
82 | [self generateSeperateLineAtCenter:CGPointMake(self.view.frame.size.width / 2, seperaterCenterY)];
83 |
84 | [self.view addSubview:windLabel];
85 | }
86 |
87 | - (void)generateSeperateLineAtCenter:(CGPoint)center {
88 | UIView *seperater = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 255, 0.5f)];
89 | seperater.backgroundColor = [UIColor lightGrayColor];
90 | seperater.center = center;
91 | [self.view addSubview:seperater];
92 | }
93 |
94 | - (UITextField *)generatePlayerNameFieldAtPoint:(CGPoint)center tintText:(NSString *)tintText {
95 | UITextField *playerTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 195, 35)];
96 | [playerTextField setFont:[MMSCFontAndColorUtil startpageFont]];
97 | playerTextField.placeholder = tintText;
98 | playerTextField.textAlignment = NSTextAlignmentCenter;
99 | playerTextField.center = center;
100 | playerTextField.clearButtonMode = UITextFieldViewModeAlways;
101 | playerTextField.delegate = self;
102 | playerTextField.returnKeyType = UIReturnKeyDone;
103 |
104 | return playerTextField;
105 | }
106 |
107 | - (UIButton *)generateStartPageButtonAtCenter:(CGPoint)center text:(NSString *)text {
108 | UIButton *button = [[UIButton alloc] init];
109 | UIImage *origImage = [UIImage imageNamed:@"resource/StartPage_ButtonBG.png"];
110 | UIImage *origClickImage = [UIImage imageNamed:@"resource/StartPage_ButtonBG_click.png"];
111 |
112 | CGFloat resizableWidth = origImage.size.width / 2.f;
113 | CGFloat resizableHeight = origImage.size.height / 2.f;
114 |
115 | UIImage *backgroundImage = [origImage resizableImageWithCapInsets:UIEdgeInsetsMake(resizableHeight, resizableWidth, resizableHeight, resizableWidth)];
116 | [button setBackgroundImage:backgroundImage forState:UIControlStateNormal];
117 |
118 | UIImage *backgroundClickImage = [origClickImage resizableImageWithCapInsets:UIEdgeInsetsMake(resizableHeight, resizableWidth, resizableHeight, resizableWidth)];
119 | [button setBackgroundImage:backgroundClickImage forState:UIControlStateSelected];
120 |
121 | [button.titleLabel setFont:[UIFont systemFontOfSize:20]];
122 | [button setTitle:text forState:UIControlStateNormal];
123 | [button setTitleColor:[MMSCFontAndColorUtil startPageLabelFontColor] forState:UIControlStateNormal];
124 | [button sizeToFit];
125 |
126 | button.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width - 2 * 40, button.frame.size.height + 5);
127 | button.center = center;
128 |
129 | return button;
130 | }
131 |
132 | - (void)viewWillAppear:(BOOL)animated {
133 | if (self.playerNames) {
134 | for (int i = 0; i < 4; i++) {
135 | MMSCPlayerViewTag tag = (MMSCPlayerViewTag)(MMSCPlayerViewTagPlayer1 + i);
136 | UITextField *playerField = [self.view viewWithTag:tag];
137 | NSString *playerName = (NSString *)self.playerNames[i];
138 | playerField.text = playerName;
139 | }
140 | }
141 | }
142 |
143 | #pragma mark ----------TextFieldDelegate----------
144 |
145 | - (BOOL)textFieldShouldReturn:(UITextField *)textField {
146 | [textField resignFirstResponder];
147 | return YES;
148 | }
149 |
150 | #pragma mark ----------button action--------------
151 |
152 | - (void)waitForRandomSeat {
153 | MMSCRandomSeatTipsController *controller = [[MMSCRandomSeatTipsController alloc] init];
154 | controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
155 |
156 | [self presentViewController:controller animated:YES completion:nil];
157 |
158 | self.playerNames = [[MMSCGameManager instance] randomSeatWithPlayers:[self getPlayerNames]];
159 | }
160 |
161 | - (void)startBattle:(id)sender {
162 | if (!self.playerNames) {
163 | [[MMSCGameManager instance] initGameWithPlayers:[self getPlayerNames]];
164 | }
165 |
166 | MMSCScoreTableController *scoreTableController = [[MMSCScoreTableController alloc] init];
167 | UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:scoreTableController];
168 | navController.navigationBar.barTintColor = [UIColor whiteColor];
169 |
170 | [self presentViewController:navController animated:YES completion:nil];
171 | }
172 |
173 | - (NSArray *)getPlayerNames {
174 | NSMutableArray *playerNames = [NSMutableArray array];
175 | for (MMSCPlayerViewTag tag = MMSCPlayerViewTagPlayer1; tag <= MMSCPlayerViewTagPlayer4; tag++) {
176 | UITextField *playerTextField = [self.view viewWithTag:tag];
177 | NSString *playerName = playerTextField.text.length > 0 ? playerTextField.text : playerTextField.placeholder;
178 | [playerNames addObject:playerName];
179 | }
180 |
181 | return [playerNames copy];
182 | }
183 |
184 | @end
185 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCRandomSeatTipsController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCRandomSeatTipsController.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/2/1.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MMSCRandomSeatTipsController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCRandomSeatTipsController.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCRandomSeatTipsController.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/2/1.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCRandomSeatTipsController.h"
10 | #import "MMSCFontAndColorUtil.h"
11 |
12 | @interface MMSCRandomSeatTipsController ()
13 |
14 | @property (nonatomic, strong) NSTimer *turnBackTimer;
15 |
16 | @end
17 |
18 | @implementation MMSCRandomSeatTipsController
19 |
20 | - (void)viewDidLoad {
21 | [super viewDidLoad];
22 |
23 | self.view.backgroundColor = [MMSCFontAndColorUtil startPageBackgroundColor];
24 |
25 | CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
26 | CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
27 |
28 | UILabel *tip1 = [[UILabel alloc] init];
29 | [tip1 setText:@"对日之前要抽风"];
30 | [tip1 setTextColor:[MMSCFontAndColorUtil startPageLabelFontColor]];
31 | [tip1 setFont:[UIFont systemFontOfSize:30 weight:4]];
32 | [tip1 sizeToFit];
33 | tip1.frame = CGRectMake(0, 0, screenWidth - 20, tip1.frame.size.height);
34 | tip1.center = CGPointMake(screenWidth / 2, screenHeight - 75);
35 | [self.view addSubview:tip1];
36 |
37 | UILabel *tip2 = [[UILabel alloc] init];
38 | [tip2 setText:@"随机座位中..."];
39 | [tip2 setTextColor:[UIColor colorWithRed:153/255.f green:156/255.f blue:168/255.f alpha:0.8]];
40 | [tip2 setFont:[UIFont systemFontOfSize:22]];
41 | [tip2 sizeToFit];
42 | tip2.frame = CGRectMake(0, 0, screenWidth - 20, tip2.frame.size.height);
43 | tip2.center = CGPointMake(screenWidth / 2, screenHeight - 40);
44 | [self.view addSubview:tip2];
45 | }
46 |
47 | - (void)viewDidAppear:(BOOL)animated {
48 | if (!_turnBackTimer) {
49 | _turnBackTimer = [NSTimer timerWithTimeInterval:1.f target:self selector:@selector(onTurnBackTimerTick) userInfo:nil repeats:NO];
50 | [[NSRunLoop mainRunLoop] addTimer:_turnBackTimer forMode:NSRunLoopCommonModes];
51 | }
52 | }
53 |
54 | - (void)onTurnBackTimerTick {
55 | [self dismissViewControllerAnimated:YES completion:nil];
56 |
57 | if (_turnBackTimer) {
58 | [_turnBackTimer invalidate];
59 | }
60 | }
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCRichiController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCRichiController.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/2/13.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol MMSCRichiDelegate
12 |
13 | - (void)richiPlayersSelected:(NSArray *)richiPlayerIndexes;
14 |
15 | @end
16 |
17 | @interface MMSCRichiController : UIViewController
18 |
19 | @property (nonatomic, weak) id delegate;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCRichiController.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCRichiController.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/2/13.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCRichiController.h"
10 | #import "MMSCGameManager.h"
11 | #import "MMSCFontAndColorUtil.h"
12 |
13 | @implementation MMSCRichiController
14 |
15 | - (void)viewDidLoad {
16 | [super viewDidLoad];
17 |
18 | UIColor *backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.6f];
19 | self.view.backgroundColor = backgroundColor;
20 |
21 | CGFloat viewWidth = 270;
22 | CGFloat viewHeight = 260;
23 | CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
24 | CGFloat screenHeigt = [UIScreen mainScreen].bounds.size.height;
25 |
26 | UIView *dialogView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, viewWidth, viewHeight)];
27 | dialogView.center = CGPointMake(screenWidth / 2.f, screenHeigt / 2.f);
28 | dialogView.backgroundColor = [UIColor whiteColor];
29 | dialogView.layer.cornerRadius = 20.f;
30 | [self.view addSubview:dialogView];
31 |
32 | CGFloat buttonHeight = 44;
33 | CGFloat seperatorX = dialogView.frame.origin.x;
34 | CGFloat seperatorWidth = dialogView.frame.size.width;
35 | CGFloat seperatorY = dialogView.frame.origin.y + viewHeight - buttonHeight;
36 | UIView *seperator = [[UIView alloc] initWithFrame:CGRectMake(seperatorX, seperatorY, seperatorWidth, 0.5f)];
37 | seperator.backgroundColor = [UIColor lightGrayColor];
38 | [self.view addSubview:seperator];
39 |
40 | UIButton *okButton = [[UIButton alloc] initWithFrame:CGRectMake(seperatorX, seperatorY, seperatorWidth, buttonHeight)];
41 | [okButton setTitle:@"ok" forState:UIControlStateNormal];
42 | [okButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
43 | [okButton addTarget:self action:@selector(okButtonClicked) forControlEvents:UIControlEventTouchUpInside];
44 |
45 | [self.view addSubview:okButton];
46 |
47 | NSArray *playerNames = [[MMSCGameManager instance] playerNames];
48 | NSArray *currentRichiPlayers = [[MMSCGameManager instance] currentRichiPlayers];
49 | CGFloat labelInterval = 45;
50 | CGFloat labelX = dialogView.frame.origin.x + 40;
51 | CGFloat switchX = dialogView.frame.origin.x + 150;
52 | CGFloat labelY = dialogView.frame.origin.y + 30;
53 | CGFloat swithcY = dialogView.frame.origin.y + 25;
54 | for (int i = 0; i < 4; i++) {
55 | UILabel *playerLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelX, labelY + labelInterval * i, 100, 20)];
56 | NSString *playerName = playerNames[i];
57 | playerLabel.backgroundColor = [UIColor clearColor];
58 | playerLabel.text = playerName;
59 | playerLabel.textAlignment = NSTextAlignmentCenter;
60 | [self.view addSubview:playerLabel];
61 |
62 | UISwitch *richiSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(switchX, swithcY + labelInterval * i, 40, 20)];
63 | richiSwitch.tag = MMSCPlayerViewTagPlayer1 + i;
64 | if ([currentRichiPlayers containsObject:@(i)]) {
65 | [richiSwitch setOn:YES];
66 | }
67 | [self.view addSubview:richiSwitch];
68 | }
69 | }
70 |
71 | - (void)okButtonClicked {
72 | NSMutableArray *richiPlayers = [NSMutableArray array];
73 |
74 | for (int i = 0; i < 4; i++) {
75 | UISwitch *richiSwitch = [self.view viewWithTag:MMSCPlayerViewTagPlayer1 + i];
76 | if ([richiSwitch isOn]) {
77 | [richiPlayers addObject:@(i)];
78 | }
79 | }
80 |
81 | if ([self.delegate respondsToSelector:@selector(richiPlayersSelected:)] ) {
82 | [self.delegate richiPlayersSelected:richiPlayers];
83 | }
84 |
85 | [self dismissViewControllerAnimated:YES completion:nil];
86 | }
87 |
88 | @end
89 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCRoundResulltController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCRoundResulltController.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/2/14.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef NS_ENUM(NSInteger, MMSCRoundResultType) {
12 | MMSCRoundResultTypeTsumo = 0x888,
13 | MMSCRoundResultTypeRon,
14 | MMSCRoundResultTypeDraw
15 | };
16 |
17 | @protocol MMSCRoundResultDelegate
18 |
19 | - (void)tsumoResultSelected:(NSUInteger)playerIndex fan:(NSInteger)fan fu:(NSInteger)fu;
20 |
21 | - (void)ronResultSelected:(NSArray *)winnerArray loser:(NSUInteger)loserIndex fan:(NSArray *)fan fu:(NSArray *)fu;
22 |
23 | - (void)drawResultSelected:(NSInteger)drawType players:(NSArray *)players oyaTenpai:(BOOL)oyaTenpai;
24 |
25 | @end
26 |
27 | @interface MMSCRoundResulltController : UIViewController
28 |
29 | @property (nonatomic, assign) MMSCRoundResultType type;
30 |
31 | @property (nonatomic, weak) id delegate;
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCScoreTableCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCScoreTableCell.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/2/10.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MMSCScoreTableCell : UITableViewCell
12 |
13 | - (void)setScoreChange:(NSArray *)scoreChanges;
14 |
15 | - (void)markRichiAtIndexes:(NSArray *)playerIndexes currentRichiPlayers:(NSArray *)currentRichiPlayer;
16 |
17 | - (void)clearRichiMark;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCScoreTableCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCScoreTableCell.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/2/10.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import "MMSCScoreTableCell.h"
10 | #import "MMSCScoreTableController.h"
11 | #import "MMSCFontAndColorUtil.h"
12 |
13 | #define MMSCRICHIMASKVIEWTAGOFFSET 0x233
14 |
15 | @implementation MMSCScoreTableCell
16 |
17 | - (void)setScoreChange:(NSArray *)scoreChanges {
18 | if ([self shouldCreateNewLabel]) {
19 | [self createScoreChangeLabels];
20 | }
21 |
22 | for (int i = 0; i < 4; i++) {
23 | UILabel *scoreLabel = [self.contentView viewWithTag:MMSCPlayerViewTagPlayer1 + i];
24 | NSInteger scoreChange = [scoreChanges[i] integerValue];
25 | NSString *scoreText = [NSString stringWithFormat:scoreChange > 0 ? @"+%zd" : @"%zd", scoreChange];
26 | scoreLabel.text = scoreText;
27 | }
28 | }
29 |
30 | - (void)createScoreChangeLabels {
31 | CGFloat interval = [UIScreen mainScreen].bounds.size.width / 4;
32 | CGFloat windLabelOffset = 0;
33 | for (int i = 0; i < 4; i++) {
34 | UILabel *scoreLabel = [[UILabel alloc] initWithFrame:CGRectMake(windLabelOffset, 2, interval, self.frame.size.height)];
35 | scoreLabel.font = [UIFont systemFontOfSize:15];
36 | scoreLabel.textAlignment = NSTextAlignmentCenter;
37 | scoreLabel.tag = (MMSCPlayerViewTag)(MMSCPlayerViewTagPlayer1 + i);
38 | [self.contentView addSubview:scoreLabel];
39 |
40 | UIView *sLine = [[UIView alloc] initWithFrame:CGRectMake(windLabelOffset + interval, 5, 0.5f, self.frame.size.height - 6)];
41 | sLine.backgroundColor = [UIColor lightGrayColor];
42 | [self.contentView addSubview:sLine];
43 |
44 | windLabelOffset += interval;
45 | }
46 | }
47 |
48 | - (BOOL)shouldCreateNewLabel {
49 | UIView *playerView = [self.contentView viewWithTag:MMSCPlayerViewTagPlayer1];
50 | return !playerView;
51 | }
52 |
53 | - (void)markRichiAtIndexes:(NSArray *)playerIndexes currentRichiPlayers:(NSArray *)currentRichiPlayerIndexes {
54 | NSMutableArray *currentRichiPlayers = [currentRichiPlayerIndexes mutableCopy];
55 | for (NSNumber *playerIndex in playerIndexes) {
56 | if ([currentRichiPlayers containsObject:playerIndex]) { // 之前已经立直过了
57 | [currentRichiPlayers removeObject:playerIndex];
58 | }
59 |
60 | [self markRichiAtIndex:playerIndex.unsignedIntegerValue];
61 | }
62 |
63 | if (currentRichiPlayers.count > 0) { // 有取消的情况发生
64 | for (NSNumber *playerIndex in currentRichiPlayers) {
65 | [self cancelRichiAtIndex:playerIndex.unsignedIntegerValue];
66 | }
67 | }
68 | }
69 |
70 | - (void)markRichiAtIndex:(NSUInteger)playerIndex {
71 | NSAssert(playerIndex < 4, @"playerIndex out of bound");
72 |
73 | MMSCPlayerViewTag labelTag = MMSCPlayerViewTagPlayer1 + playerIndex;
74 | UILabel *playerLabel = [self.contentView viewWithTag:labelTag];
75 | UIImageView *richiMaskView = [self.contentView viewWithTag:labelTag + MMSCRICHIMASKVIEWTAGOFFSET];
76 |
77 | if (!richiMaskView) {
78 | richiMaskView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, playerLabel.frame.size.width - 5, self.frame.size.height - 14)];
79 | richiMaskView.backgroundColor = [UIColor clearColor];
80 | richiMaskView.image = [UIImage imageNamed:@"resource/richiIcon-horizontal.png"];
81 | richiMaskView.alpha = 0.6f;
82 | richiMaskView.tag = labelTag + MMSCRICHIMASKVIEWTAGOFFSET;
83 | [self.contentView addSubview:richiMaskView];
84 | }
85 |
86 | richiMaskView.hidden = NO;
87 | richiMaskView.center = playerLabel.center;
88 | [self.contentView bringSubviewToFront:playerLabel];
89 | }
90 |
91 | - (void)cancelRichiAtIndex:(NSInteger)playerIndex {
92 | NSAssert(playerIndex < 4, @"playerIndex out of bound");
93 |
94 | UIImageView *richiMaskView = [self.contentView viewWithTag:MMSCPlayerViewTagPlayer1 + playerIndex + MMSCRICHIMASKVIEWTAGOFFSET];
95 |
96 | richiMaskView.hidden = YES;
97 | }
98 |
99 | - (void)clearRichiMark {
100 | for (int i = 0; i < 4; i++) {
101 | [self cancelRichiAtIndex:i];
102 | }
103 | }
104 |
105 | @end
106 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/View/MMSCScoreTableController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMSCScoreTableController.h
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/2/7.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MMSCScoreTableController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // MahjongScoreCalculator
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/StartPage_ButtonBG@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/StartPage_ButtonBG@2x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/StartPage_ButtonBG_click@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/StartPage_ButtonBG_click@2x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/mahjong-appicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/mahjong-appicon.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/menu.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/more_icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/more_icon@2x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/more_icon_click@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/more_icon_click@2x.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/richiIcon-horizontal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/richiIcon-horizontal.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/richiIcon-vertical.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mahosyojyo/Majong-AutoScore/a4ec15c2172f5dcf26d37c17631a9bd7b76f9a17/iOS/src/MahjongScoreCalculator/MahjongScoreCalculator/resource/richiIcon-vertical.png
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculatorTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/iOS/src/MahjongScoreCalculator/MahjongScoreCalculatorTests/MahjongScoreCalculatorTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // MahjongScoreCalculatorTests.m
3 | // MahjongScoreCalculatorTests
4 | //
5 | // Created by fredfx on 16/1/2.
6 | // Copyright © 2016年 fredfx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MahjongScoreCalculatorTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation MahjongScoreCalculatorTests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | - (void)tearDown {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | [super tearDown];
25 | }
26 |
27 | - (void)testExample {
28 | // This is an example of a functional test case.
29 | // Use XCTAssert and related functions to verify your tests produce the correct results.
30 | }
31 |
32 | - (void)testPerformanceExample {
33 | // This is an example of a performance test case.
34 | [self measureBlock:^{
35 | // Put the code you want to measure the time of here.
36 | }];
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------