└── Landlords
├── .gitignore
├── .project
├── AndroidManifest.xml
├── LICENSE
├── assets
├── bkg_btn_normal.png
├── bkg_btn_pressed.png
├── bkg_table.png
├── boom.wav
├── buyao.wav
├── c10.png
├── c10s.png
├── c11.png
├── c11s.png
├── c12.png
├── c12s.png
├── c13.png
├── c13s.png
├── c14.png
├── c14s.png
├── c15.png
├── c15s.png
├── c3.png
├── c3s.png
├── c4.png
├── c4s.png
├── c5.png
├── c5s.png
├── c6.png
├── c6s.png
├── c7.png
├── c7s.png
├── c8.png
├── c8s.png
├── c9.png
├── c9s.png
├── cardbg.png
├── d10.png
├── d10s.png
├── d11.png
├── d11s.png
├── d12.png
├── d12s.png
├── d13.png
├── d13s.png
├── d14.png
├── d14s.png
├── d15.png
├── d15s.png
├── d3.png
├── d3s.png
├── d4.png
├── d4s.png
├── d5.png
├── d5s.png
├── d6.png
├── d6s.png
├── d7.png
├── d7s.png
├── d8.png
├── d8s.png
├── d9.png
├── d9s.png
├── dani1.wav
├── dani2.wav
├── dani3.wav
├── dawang.wav
├── feiji.wav
├── givecard.wav
├── h10.png
├── h10s.png
├── h11.png
├── h11s.png
├── h12.png
├── h12s.png
├── h13.png
├── h13s.png
├── h14.png
├── h14s.png
├── h15.png
├── h15s.png
├── h3.png
├── h3s.png
├── h4.png
├── h4s.png
├── h5.png
├── h5s.png
├── h6.png
├── h6s.png
├── h7.png
├── h7s.png
├── h8.png
├── h8s.png
├── h9.png
├── h9s.png
├── icLandlord.png
├── j1.png
├── j2.png
├── landlord_p1.mp3
├── landlord_p1.png
├── landlord_p2.mp3
├── landlord_p2.png
├── landlord_p3.mp3
├── landlord_p3.png
├── landlord_pass.mp3
├── landlord_pass.png
├── liandui.wav
├── lose.mp3
├── music_off.png
├── music_on.png
├── noBiggerCards.png
├── numbers.png
├── pass.wav
├── plane.wav
├── play_ahand.png
├── play_pass.png
├── play_rechoose.png
├── play_tip.png
├── playerHuman.png
├── playerLeft.png
├── playerRight.png
├── rechoose.mp3
├── s10.png
├── s10s.png
├── s11.png
├── s11s.png
├── s12.png
├── s12s.png
├── s13.png
├── s13s.png
├── s14.png
├── s14s.png
├── s15.png
├── s15s.png
├── s3.png
├── s3s.png
├── s4.png
├── s4s.png
├── s5.png
├── s5s.png
├── s6.png
├── s6s.png
├── s7.png
├── s7s.png
├── s8.png
├── s8s.png
├── s9.png
├── s9s.png
├── sandaiyi.wav
├── sandaiyidui.wav
├── shunzi.wav
├── sidaier.wav
├── sidailiangdui.wav
├── sound_off.png
├── sound_on.png
├── start.wav
├── wangzha.wav
├── win.mp3
├── wrongCards.png
├── xiaowang.wav
├── yaobuqi.wav
└── zhadan.wav
├── ic_launcher-web.png
├── libs
└── android-support-v4.jar
├── lint.xml
├── proguard-project.txt
├── project.properties
├── res
├── drawable-xhdpi
│ ├── bkg_btn_normal.png
│ ├── bkg_table.png
│ └── ic_launcher.png
├── layout
│ ├── activity_loading.xml
│ └── activity_main.xml
├── values-v11
│ └── styles.xml
├── values-v14
│ └── styles.xml
└── values
│ ├── strings.xml
│ └── styles.xml
└── src
└── com
└── mym
├── landlords
├── ai
│ ├── AI.java
│ ├── Game.java
│ ├── Player.java
│ ├── PlayerCardsInfo.java
│ ├── StraightAnalyst.java
│ ├── StraightNumbers.java
│ └── TipRobot.java
├── card
│ ├── Airplane.java
│ ├── Bomb.java
│ ├── BombType.java
│ ├── Card.java
│ ├── CardFactory.java
│ ├── CardSuit.java
│ ├── CardType.java
│ ├── DoubleStraight.java
│ ├── HandCard.java
│ ├── NonBombType.java
│ ├── Pair.java
│ ├── Rocket.java
│ ├── Single.java
│ ├── Straight.java
│ └── Three.java
├── res
│ ├── Assets.java
│ ├── GameGraphics.java
│ ├── GlobalSoundPool.java
│ └── LiveBitmap.java
├── test
│ └── SpecifiedHandCardGen.java
├── ui
│ ├── LoadingActivity.java
│ ├── MainActivity.java
│ └── Settings.java
└── widget
│ ├── BitmapButton.java
│ ├── BitmapView.java
│ ├── GameScreen.java
│ ├── GameView.java
│ ├── MappedTouchEvent.java
│ └── RedrawableView.java
└── util
├── BitmapUtil.java
├── LangUtils.java
└── PollingThread.java
/Landlords/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
28 | # Eclipse files
29 | *.prefs
30 | *.classpath
--------------------------------------------------------------------------------
/Landlords/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Landlords
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Landlords/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Landlords/assets/bkg_btn_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/bkg_btn_normal.png
--------------------------------------------------------------------------------
/Landlords/assets/bkg_btn_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/bkg_btn_pressed.png
--------------------------------------------------------------------------------
/Landlords/assets/bkg_table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/bkg_table.png
--------------------------------------------------------------------------------
/Landlords/assets/boom.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/boom.wav
--------------------------------------------------------------------------------
/Landlords/assets/buyao.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/buyao.wav
--------------------------------------------------------------------------------
/Landlords/assets/c10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c10.png
--------------------------------------------------------------------------------
/Landlords/assets/c10s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c10s.png
--------------------------------------------------------------------------------
/Landlords/assets/c11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c11.png
--------------------------------------------------------------------------------
/Landlords/assets/c11s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c11s.png
--------------------------------------------------------------------------------
/Landlords/assets/c12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c12.png
--------------------------------------------------------------------------------
/Landlords/assets/c12s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c12s.png
--------------------------------------------------------------------------------
/Landlords/assets/c13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c13.png
--------------------------------------------------------------------------------
/Landlords/assets/c13s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c13s.png
--------------------------------------------------------------------------------
/Landlords/assets/c14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c14.png
--------------------------------------------------------------------------------
/Landlords/assets/c14s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c14s.png
--------------------------------------------------------------------------------
/Landlords/assets/c15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c15.png
--------------------------------------------------------------------------------
/Landlords/assets/c15s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c15s.png
--------------------------------------------------------------------------------
/Landlords/assets/c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c3.png
--------------------------------------------------------------------------------
/Landlords/assets/c3s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c3s.png
--------------------------------------------------------------------------------
/Landlords/assets/c4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c4.png
--------------------------------------------------------------------------------
/Landlords/assets/c4s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c4s.png
--------------------------------------------------------------------------------
/Landlords/assets/c5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c5.png
--------------------------------------------------------------------------------
/Landlords/assets/c5s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c5s.png
--------------------------------------------------------------------------------
/Landlords/assets/c6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c6.png
--------------------------------------------------------------------------------
/Landlords/assets/c6s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c6s.png
--------------------------------------------------------------------------------
/Landlords/assets/c7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c7.png
--------------------------------------------------------------------------------
/Landlords/assets/c7s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c7s.png
--------------------------------------------------------------------------------
/Landlords/assets/c8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c8.png
--------------------------------------------------------------------------------
/Landlords/assets/c8s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c8s.png
--------------------------------------------------------------------------------
/Landlords/assets/c9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c9.png
--------------------------------------------------------------------------------
/Landlords/assets/c9s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/c9s.png
--------------------------------------------------------------------------------
/Landlords/assets/cardbg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/cardbg.png
--------------------------------------------------------------------------------
/Landlords/assets/d10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d10.png
--------------------------------------------------------------------------------
/Landlords/assets/d10s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d10s.png
--------------------------------------------------------------------------------
/Landlords/assets/d11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d11.png
--------------------------------------------------------------------------------
/Landlords/assets/d11s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d11s.png
--------------------------------------------------------------------------------
/Landlords/assets/d12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d12.png
--------------------------------------------------------------------------------
/Landlords/assets/d12s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d12s.png
--------------------------------------------------------------------------------
/Landlords/assets/d13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d13.png
--------------------------------------------------------------------------------
/Landlords/assets/d13s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d13s.png
--------------------------------------------------------------------------------
/Landlords/assets/d14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d14.png
--------------------------------------------------------------------------------
/Landlords/assets/d14s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d14s.png
--------------------------------------------------------------------------------
/Landlords/assets/d15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d15.png
--------------------------------------------------------------------------------
/Landlords/assets/d15s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d15s.png
--------------------------------------------------------------------------------
/Landlords/assets/d3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d3.png
--------------------------------------------------------------------------------
/Landlords/assets/d3s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d3s.png
--------------------------------------------------------------------------------
/Landlords/assets/d4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d4.png
--------------------------------------------------------------------------------
/Landlords/assets/d4s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d4s.png
--------------------------------------------------------------------------------
/Landlords/assets/d5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d5.png
--------------------------------------------------------------------------------
/Landlords/assets/d5s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d5s.png
--------------------------------------------------------------------------------
/Landlords/assets/d6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d6.png
--------------------------------------------------------------------------------
/Landlords/assets/d6s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d6s.png
--------------------------------------------------------------------------------
/Landlords/assets/d7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d7.png
--------------------------------------------------------------------------------
/Landlords/assets/d7s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d7s.png
--------------------------------------------------------------------------------
/Landlords/assets/d8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d8.png
--------------------------------------------------------------------------------
/Landlords/assets/d8s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d8s.png
--------------------------------------------------------------------------------
/Landlords/assets/d9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d9.png
--------------------------------------------------------------------------------
/Landlords/assets/d9s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/d9s.png
--------------------------------------------------------------------------------
/Landlords/assets/dani1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/dani1.wav
--------------------------------------------------------------------------------
/Landlords/assets/dani2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/dani2.wav
--------------------------------------------------------------------------------
/Landlords/assets/dani3.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/dani3.wav
--------------------------------------------------------------------------------
/Landlords/assets/dawang.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/dawang.wav
--------------------------------------------------------------------------------
/Landlords/assets/feiji.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/feiji.wav
--------------------------------------------------------------------------------
/Landlords/assets/givecard.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/givecard.wav
--------------------------------------------------------------------------------
/Landlords/assets/h10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h10.png
--------------------------------------------------------------------------------
/Landlords/assets/h10s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h10s.png
--------------------------------------------------------------------------------
/Landlords/assets/h11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h11.png
--------------------------------------------------------------------------------
/Landlords/assets/h11s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h11s.png
--------------------------------------------------------------------------------
/Landlords/assets/h12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h12.png
--------------------------------------------------------------------------------
/Landlords/assets/h12s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h12s.png
--------------------------------------------------------------------------------
/Landlords/assets/h13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h13.png
--------------------------------------------------------------------------------
/Landlords/assets/h13s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h13s.png
--------------------------------------------------------------------------------
/Landlords/assets/h14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h14.png
--------------------------------------------------------------------------------
/Landlords/assets/h14s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h14s.png
--------------------------------------------------------------------------------
/Landlords/assets/h15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h15.png
--------------------------------------------------------------------------------
/Landlords/assets/h15s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h15s.png
--------------------------------------------------------------------------------
/Landlords/assets/h3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h3.png
--------------------------------------------------------------------------------
/Landlords/assets/h3s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h3s.png
--------------------------------------------------------------------------------
/Landlords/assets/h4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h4.png
--------------------------------------------------------------------------------
/Landlords/assets/h4s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h4s.png
--------------------------------------------------------------------------------
/Landlords/assets/h5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h5.png
--------------------------------------------------------------------------------
/Landlords/assets/h5s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h5s.png
--------------------------------------------------------------------------------
/Landlords/assets/h6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h6.png
--------------------------------------------------------------------------------
/Landlords/assets/h6s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h6s.png
--------------------------------------------------------------------------------
/Landlords/assets/h7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h7.png
--------------------------------------------------------------------------------
/Landlords/assets/h7s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h7s.png
--------------------------------------------------------------------------------
/Landlords/assets/h8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h8.png
--------------------------------------------------------------------------------
/Landlords/assets/h8s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h8s.png
--------------------------------------------------------------------------------
/Landlords/assets/h9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h9.png
--------------------------------------------------------------------------------
/Landlords/assets/h9s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/h9s.png
--------------------------------------------------------------------------------
/Landlords/assets/icLandlord.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/icLandlord.png
--------------------------------------------------------------------------------
/Landlords/assets/j1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/j1.png
--------------------------------------------------------------------------------
/Landlords/assets/j2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/j2.png
--------------------------------------------------------------------------------
/Landlords/assets/landlord_p1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/landlord_p1.mp3
--------------------------------------------------------------------------------
/Landlords/assets/landlord_p1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/landlord_p1.png
--------------------------------------------------------------------------------
/Landlords/assets/landlord_p2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/landlord_p2.mp3
--------------------------------------------------------------------------------
/Landlords/assets/landlord_p2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/landlord_p2.png
--------------------------------------------------------------------------------
/Landlords/assets/landlord_p3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/landlord_p3.mp3
--------------------------------------------------------------------------------
/Landlords/assets/landlord_p3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/landlord_p3.png
--------------------------------------------------------------------------------
/Landlords/assets/landlord_pass.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/landlord_pass.mp3
--------------------------------------------------------------------------------
/Landlords/assets/landlord_pass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/landlord_pass.png
--------------------------------------------------------------------------------
/Landlords/assets/liandui.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/liandui.wav
--------------------------------------------------------------------------------
/Landlords/assets/lose.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/lose.mp3
--------------------------------------------------------------------------------
/Landlords/assets/music_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/music_off.png
--------------------------------------------------------------------------------
/Landlords/assets/music_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/music_on.png
--------------------------------------------------------------------------------
/Landlords/assets/noBiggerCards.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/noBiggerCards.png
--------------------------------------------------------------------------------
/Landlords/assets/numbers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/numbers.png
--------------------------------------------------------------------------------
/Landlords/assets/pass.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/pass.wav
--------------------------------------------------------------------------------
/Landlords/assets/plane.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/plane.wav
--------------------------------------------------------------------------------
/Landlords/assets/play_ahand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/play_ahand.png
--------------------------------------------------------------------------------
/Landlords/assets/play_pass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/play_pass.png
--------------------------------------------------------------------------------
/Landlords/assets/play_rechoose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/play_rechoose.png
--------------------------------------------------------------------------------
/Landlords/assets/play_tip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/play_tip.png
--------------------------------------------------------------------------------
/Landlords/assets/playerHuman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/playerHuman.png
--------------------------------------------------------------------------------
/Landlords/assets/playerLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/playerLeft.png
--------------------------------------------------------------------------------
/Landlords/assets/playerRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/playerRight.png
--------------------------------------------------------------------------------
/Landlords/assets/rechoose.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/rechoose.mp3
--------------------------------------------------------------------------------
/Landlords/assets/s10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s10.png
--------------------------------------------------------------------------------
/Landlords/assets/s10s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s10s.png
--------------------------------------------------------------------------------
/Landlords/assets/s11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s11.png
--------------------------------------------------------------------------------
/Landlords/assets/s11s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s11s.png
--------------------------------------------------------------------------------
/Landlords/assets/s12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s12.png
--------------------------------------------------------------------------------
/Landlords/assets/s12s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s12s.png
--------------------------------------------------------------------------------
/Landlords/assets/s13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s13.png
--------------------------------------------------------------------------------
/Landlords/assets/s13s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s13s.png
--------------------------------------------------------------------------------
/Landlords/assets/s14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s14.png
--------------------------------------------------------------------------------
/Landlords/assets/s14s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s14s.png
--------------------------------------------------------------------------------
/Landlords/assets/s15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s15.png
--------------------------------------------------------------------------------
/Landlords/assets/s15s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s15s.png
--------------------------------------------------------------------------------
/Landlords/assets/s3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s3.png
--------------------------------------------------------------------------------
/Landlords/assets/s3s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s3s.png
--------------------------------------------------------------------------------
/Landlords/assets/s4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s4.png
--------------------------------------------------------------------------------
/Landlords/assets/s4s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s4s.png
--------------------------------------------------------------------------------
/Landlords/assets/s5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s5.png
--------------------------------------------------------------------------------
/Landlords/assets/s5s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s5s.png
--------------------------------------------------------------------------------
/Landlords/assets/s6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s6.png
--------------------------------------------------------------------------------
/Landlords/assets/s6s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s6s.png
--------------------------------------------------------------------------------
/Landlords/assets/s7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s7.png
--------------------------------------------------------------------------------
/Landlords/assets/s7s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s7s.png
--------------------------------------------------------------------------------
/Landlords/assets/s8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s8.png
--------------------------------------------------------------------------------
/Landlords/assets/s8s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s8s.png
--------------------------------------------------------------------------------
/Landlords/assets/s9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s9.png
--------------------------------------------------------------------------------
/Landlords/assets/s9s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/s9s.png
--------------------------------------------------------------------------------
/Landlords/assets/sandaiyi.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/sandaiyi.wav
--------------------------------------------------------------------------------
/Landlords/assets/sandaiyidui.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/sandaiyidui.wav
--------------------------------------------------------------------------------
/Landlords/assets/shunzi.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/shunzi.wav
--------------------------------------------------------------------------------
/Landlords/assets/sidaier.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/sidaier.wav
--------------------------------------------------------------------------------
/Landlords/assets/sidailiangdui.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/sidailiangdui.wav
--------------------------------------------------------------------------------
/Landlords/assets/sound_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/sound_off.png
--------------------------------------------------------------------------------
/Landlords/assets/sound_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/sound_on.png
--------------------------------------------------------------------------------
/Landlords/assets/start.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/start.wav
--------------------------------------------------------------------------------
/Landlords/assets/wangzha.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/wangzha.wav
--------------------------------------------------------------------------------
/Landlords/assets/win.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/win.mp3
--------------------------------------------------------------------------------
/Landlords/assets/wrongCards.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/wrongCards.png
--------------------------------------------------------------------------------
/Landlords/assets/xiaowang.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/xiaowang.wav
--------------------------------------------------------------------------------
/Landlords/assets/yaobuqi.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/yaobuqi.wav
--------------------------------------------------------------------------------
/Landlords/assets/zhadan.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/assets/zhadan.wav
--------------------------------------------------------------------------------
/Landlords/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/ic_launcher-web.png
--------------------------------------------------------------------------------
/Landlords/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/Landlords/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Landlords/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/Landlords/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-20
15 |
--------------------------------------------------------------------------------
/Landlords/res/drawable-xhdpi/bkg_btn_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/res/drawable-xhdpi/bkg_btn_normal.png
--------------------------------------------------------------------------------
/Landlords/res/drawable-xhdpi/bkg_table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/res/drawable-xhdpi/bkg_table.png
--------------------------------------------------------------------------------
/Landlords/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muyangmin/Landlords/4bce1105431950ea90ebe52027d6c31bfd42b694/Landlords/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Landlords/res/layout/activity_loading.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
15 |
16 |
22 |
23 |
30 |
31 |
36 |
37 |
43 |
44 |
48 |
49 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/Landlords/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Landlords/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Landlords/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Landlords/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Landlords
5 | Hello world!
6 | LoadingActivity
7 | 音效:打开
8 | 音效:关闭
9 | 开始游戏
10 | 分享游戏
11 | 分享游戏
12 | 发现一个好玩的斗地主游戏,快来和我一起玩吧!http://t.cn/R2Sq4cQ
13 |
14 |
--------------------------------------------------------------------------------
/Landlords/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
35 |
36 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/ai/AI.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.ai;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.Collections;
6 | import java.util.Iterator;
7 | import java.util.List;
8 |
9 | import android.util.Log;
10 |
11 | import com.mym.landlords.card.Airplane;
12 | import com.mym.landlords.card.Bomb;
13 | import com.mym.landlords.card.BombType;
14 | import com.mym.landlords.card.Card;
15 | import com.mym.landlords.card.CardSuit;
16 | import com.mym.landlords.card.CardType;
17 | import com.mym.landlords.card.DoubleStraight;
18 | import com.mym.landlords.card.NonBombType;
19 | import com.mym.landlords.card.Pair;
20 | import com.mym.landlords.card.Rocket;
21 | import com.mym.landlords.card.Single;
22 | import com.mym.landlords.card.Straight;
23 | import com.mym.landlords.card.Three;
24 | import com.mym.util.LangUtils;
25 |
26 | /**
27 | * 处理游戏的AI逻辑。
28 | *
所有的字段和方法访问权限均为包级别访问,即外界不可见。方法会通过Player类进行封装,对外界透明。
29 | * 此外,所有的方法均不做为静态,主要是为了AI的定制化和多样化考虑,可以引入策略模式使得AI更灵活多变。
30 | * @author Muyangmin
31 | * @create 2015-3-21
32 | */
33 | final class AI {
34 |
35 | private final String LOG_TAG/* = "AI"*/;
36 | /**
37 | * 保存关联的AI Player对象。
38 | */
39 | private final Player bindPlayer;
40 |
41 | //package access
42 | AI(Player player){
43 | bindPlayer = player;
44 | LOG_TAG = (bindPlayer==null || bindPlayer.getPlayerName()==null)
45 | ? "AI" : bindPlayer.getPlayerName();
46 | }
47 |
48 | /**
49 | * 按照牌张大小和牌中炸弹数量分析叫牌的分数。
50 | * 基本算法如下:王炸记8分,大王4分,小王3分,每个2记2分,每个A记1分,炸弹每个记3分。
51 | * 全部加起来后看最后得分,大于8分即可叫3分,大于5分可叫2分,大于3分可叫1分,否则不叫。
52 | * @return 返回0~3之间的某个数值。
53 | */
54 | private final int callLandlord(ArrayList list){
55 | //force sort
56 | Collections.sort(list, Card.COMPARATOR_WITH_SUIT);
57 | int evaluateScore = 0;
58 | int size = list.size();
59 | //判断王炸和单张大小王
60 | if ( list.get(size-1).getValue()==Card.CARD_VALUE_JOKER_B
61 | && list.get(size-2).getValue()==Card.CARD_VALUE_JOKER_S ){
62 | evaluateScore += 8;
63 | }
64 | else if ( list.get(size-1).getValue()==Card.CARD_VALUE_JOKER_B ){
65 | evaluateScore += 4;
66 | }
67 | else if ( list.get(size-1).getValue()==Card.CARD_VALUE_JOKER_S ){
68 | evaluateScore += 3;
69 | }
70 | //判断2和A/K的数量
71 | for (Card card:list){
72 | switch (card.getValue()){
73 | case Card.CARD_VALUE_2: evaluateScore += 2; break;
74 | case Card.CARD_VALUE_A: evaluateScore += 1; break;
75 | case Card.CARD_VALUE_K: evaluateScore += 1; break;
76 | default: //do nothing
77 | break;
78 | }
79 | }
80 | //TODO add bomb count;
81 |
82 | Log.d(LOG_TAG, "evaluateScore="+evaluateScore);
83 |
84 | if ( evaluateScore > 8){
85 | return Game.BASIC_SCORE_THREE;
86 | }
87 | else if (evaluateScore > 5){
88 | return Game.BASIC_SCORE_TWO;
89 | }
90 | else if (evaluateScore > 3){
91 | return Game.BASIC_SCORE_ONE;
92 | }
93 | else{
94 | return Game.BASIC_SCORE_NONE;
95 | }
96 | }
97 |
98 | /**
99 | * 返回叫牌信息。
100 | * @param cards 手牌列表。
101 | * @param minScore 最低分(不包含)。这个分数通常是上一个玩家叫的分数。
102 | * @return 返回一个比minScore更大的分数,或者 是{@link Game#BASIC_BASIC_SCORE_NONE},表示不叫。
103 | */
104 | protected final int callLandlord(ArrayList cards, int minScore) {
105 | int alalysis = callLandlord(cards);
106 | return alalysis > minScore ? alalysis : Game.BASIC_SCORE_NONE;
107 | }
108 |
109 | protected CardType followCards(CardType lastType){
110 | CardType decideType = null;
111 | ArrayList cardTypes = bindPlayer.cardsInfo.cardTypes;
112 | if (lastType==null){
113 | //出第一手牌,从最小的打起
114 | decideType = (cardTypes.get(0));
115 | //如果是三条,则考虑是否带牌
116 | if (decideType instanceof Three){
117 | optimizeThreeAttachments((Three) decideType, cardTypes);
118 | }
119 | }
120 | else{
121 | //先判断是否需要跟牌
122 | boolean needToForce = needToFollow(lastType);
123 | if (lastType instanceof Single){
124 | decideType = followSingle((Single) lastType, needToForce);
125 | }
126 | else if (lastType instanceof Pair){
127 | decideType = followPair((Pair) lastType, needToForce);
128 | }
129 | else if (lastType instanceof Three){
130 | decideType = followThree((Three) lastType, needToForce);
131 | }
132 | else if (lastType instanceof Straight){
133 | decideType = followStraights((Straight) lastType, needToForce);
134 | }
135 | else if (lastType instanceof DoubleStraight){
136 | decideType = followDoubleStraights((DoubleStraight)lastType, needToForce);
137 | }
138 | else {
139 | for (CardType type : cardTypes) {
140 | if (type.canAgainstType(lastType)) {
141 | decideType = type;
142 | break;
143 | }
144 | }
145 | }
146 | }
147 | return decideType;
148 | }
149 |
150 | private CardType findBombType(PlayerCardsInfo info, NonBombType followType){
151 | ArrayList cardTypes = info.cardTypes;
152 | if (info.bombCount > 0) {
153 | for (CardType type : cardTypes) {
154 | if (type instanceof BombType) {
155 | return type;
156 | }
157 | }
158 | }
159 | return null;
160 | }
161 |
162 | /**
163 | * 处理顺子的跟牌策略。
164 | */
165 | private CardType followStraights(Straight followType, boolean needToForce){
166 | PlayerCardsInfo info = bindPlayer.cardsInfo;
167 | ArrayList cardTypes = info.cardTypes;
168 | // 如果有现成的单牌且比原来的大,则返回
169 | for (CardType type : cardTypes) {
170 | if (type instanceof Straight && type.canAgainstType(followType)) {
171 | return type;
172 | }
173 | }
174 | // 没有,则找炸弹
175 | CardType bomb = findBombType(info, followType);
176 | if (bomb!=null){
177 | return bomb;
178 | }
179 | //如果不强制跟牌,则到此为止
180 | if (!needToForce){
181 | return null;
182 | }
183 | //拆牌跟
184 | ArrayList list = StraightAnalyst.forceGetStraights(
185 | followType, bindPlayer.getHandCards());
186 | return list==null || list.isEmpty()? null : list.get(0);
187 | }
188 |
189 | /**
190 | * 处理连对的跟牌策略。
191 | */
192 | private CardType followDoubleStraights(DoubleStraight followType, boolean needToForce){
193 | Log.d(LOG_TAG, "followDoubleStraights:"+needToForce);
194 | PlayerCardsInfo info = bindPlayer.cardsInfo;
195 | ArrayList cardTypes = info.cardTypes;
196 | // 如果有现成的单牌且比原来的大,则返回
197 | for (CardType type : cardTypes) {
198 | if (type instanceof DoubleStraight && type.canAgainstType(followType)) {
199 | return type;
200 | }
201 | }
202 | // 没有,则找炸弹
203 | CardType bomb = findBombType(info, followType);
204 | if (bomb!=null){
205 | return bomb;
206 | }
207 | //如果不强制跟牌,则到此为止
208 | if (!needToForce){
209 | return null;
210 | }
211 | Log.d(LOG_TAG, "force :"+bindPlayer.getHandCards());
212 | //拆牌跟
213 | ArrayList list = StraightAnalyst.forceGetDoubleStraights(
214 | followType, bindPlayer.getHandCards());
215 | return list==null || list.isEmpty()? null : list.get(0);
216 | }
217 |
218 | /**
219 | * 处理单牌的跟牌方案。
220 | *
221 | * @param followType
222 | * 需要被跟的牌
223 | * @param needToForce
224 | * 是否需要拆牌(强制跟牌)
225 | * @return 如果有牌可出,则返回这个牌型对象,否则返回null。注意:返回的不一定是Single对象,还可能是炸弹。
226 | */
227 | private CardType followSingle(Single followType, boolean needToForce) {
228 | Card followCard = followType.getCardList().get(0); // 取出要跟的牌,便于比较
229 | PlayerCardsInfo info = bindPlayer.cardsInfo;
230 | ArrayList cardTypes = info.cardTypes;
231 | if (cardTypes == null) {
232 | return null;
233 | }
234 | // 如果有现成的单牌且比原来的大,则返回
235 | for (CardType type : cardTypes) {
236 | if (type instanceof Single && type.compareTo(followType) > 0) {
237 | return type;
238 | }
239 | }
240 | // 没有,则拆2.注意:如果有王炸则不拆,而如果没有王炸,其必定是单牌,已在前面出过。
241 | if (info.twoAndJokerCount > 0) {
242 | for (Card card : bindPlayer.getHandCards()) {
243 | if (card.getValue() == Card.CARD_VALUE_2
244 | && card.compareTo(followCard) > 0) {
245 | return new Single(card);
246 | }
247 | }
248 | }
249 | // 没有,则找炸弹
250 | CardType bomb = findBombType(info, followType);
251 | if (bomb!=null){
252 | return bomb;
253 | }
254 | // 如果不是强制跟牌,则到此为止
255 | if (!needToForce) {
256 | return null;
257 | }
258 | // 否则拆顺子的顶牌, 5张以下顺子不拆
259 | for (CardType type : cardTypes) {
260 | if (type instanceof Straight) {
261 | int length = ((Straight) type).length;
262 | if (length <= 5) {
263 | continue;
264 | }
265 | Card lastCard = type.getCardList().get(length - 1);
266 | if (lastCard.compareTo(followCard) > 0) {
267 | return new Single(lastCard);
268 | }
269 | }
270 | }
271 | // 否则拆三条
272 | for (CardType type : cardTypes) {
273 | if (type instanceof Three) {
274 | // important:只能拆BodyList,而不能拆其带的牌
275 | Card card = ((Three) type).getBodyList().get(0);
276 | if (card.compareTo(followCard) > 0) {
277 | return new Single(card);
278 | }
279 | }
280 | }
281 | //如果仅有两张牌且为对子,则拆牌单出(如果不是对子则必然在前面已经单出)
282 | if (cardTypes.size()==1){
283 | Card card = bindPlayer.getHandCards().get(0);
284 | if (card.compareTo(followCard)>0){
285 | return new Single(card);
286 | }
287 | }
288 | // 无牌可出
289 | return null;
290 | }
291 |
292 | /**
293 | * 处理对子的跟牌方案。
294 | *
295 | * @param followType
296 | * 需要被跟的牌
297 | * @param needToForce
298 | * 是否需要拆牌(强制跟牌)
299 | * @return 如果有牌可出,则返回这个牌型对象,否则返回null。注意:返回的不一定是Pair对象,还可能是炸弹。
300 | */
301 | private CardType followPair(Pair followType, boolean needToForce) {
302 | Card followCard = followType.getCardList().get(0); // 取出要跟的牌,便于比较
303 | PlayerCardsInfo info = bindPlayer.cardsInfo;
304 | ArrayList cardTypes = info.cardTypes;
305 | if (cardTypes == null) {
306 | return null;
307 | }
308 | // 如果有现成的对子且比原来的大,则返回
309 | for (CardType type : cardTypes) {
310 | if (type instanceof Pair && type.compareTo(followType) > 0) {
311 | return type;
312 | }
313 | }
314 | // 没有,则拆2.注意:王炸不拆
315 | if (info.twoAndJokerCount > 1) {
316 | ArrayList cards = takeoutCards(new int[] { Card.CARD_VALUE_2,
317 | Card.CARD_VALUE_2 }, bindPlayer.getHandCards());
318 | if (cards != null) {
319 | return new Pair(cards);
320 | }
321 | }
322 | // 没有,则找炸弹
323 | CardType bomb = findBombType(info, followType);
324 | if (bomb!=null){
325 | return bomb;
326 | }
327 | // 如果不是强制跟牌,则到此为止
328 | if (!needToForce) {
329 | return null;
330 | }
331 | // 否则拆连对,暂略
332 | // 否则拆三条
333 | for (CardType type : cardTypes) {
334 | if (type instanceof Three) {
335 | Card card = ((Three) type).getBodyList().get(0);
336 | //仅比较点数
337 | if (card.compareTo(followCard) > 0) {
338 | return new Pair(new ArrayList(((Three) type)
339 | .getBodyList().subList(0, 2)));
340 | }
341 | }
342 | }
343 | // 还是没有牌,不出
344 | return null;
345 | }
346 |
347 | /**
348 | * 处理三条的跟牌方案。
349 | *
350 | * @param followType
351 | * 需要被跟的牌
352 | * @param needToForce
353 | * 是否需要拆牌(强制跟牌)
354 | * @return 如果有牌可出,则返回这个牌型对象,否则返回null。注意:返回的不一定是Three对象,还可能是炸弹。
355 | */
356 | private CardType followThree(Three followType, boolean needToForce) {
357 | PlayerCardsInfo info = bindPlayer.cardsInfo;
358 | ArrayList cardTypes = info.cardTypes;
359 | // 先确定要带的牌,避免在循环中处理
360 | CardType attachType = null;
361 | boolean hasRightAttachment = false;
362 | if (followType.getAttachType() instanceof Single) {
363 | // This is a hack. we just create a "least" card and follow it.
364 | attachType = followSingle(new Single(new Card(CardSuit.Spade,
365 | Card.CARD_VALUE_3)), needToForce);
366 | if ( (attachType!=null) && (attachType instanceof Single)) {
367 | hasRightAttachment = true;
368 | }
369 | } else if (followType.getAttachType() instanceof Pair) {
370 | attachType = followPair(
371 | new Pair(LangUtils.createList(new Card(CardSuit.Spade,
372 | Card.CARD_VALUE_3), new Card(CardSuit.Spade,
373 | Card.CARD_VALUE_3))), needToForce);
374 | if ( (attachType!=null) && (attachType instanceof Pair)) {
375 | hasRightAttachment = true;
376 | }
377 | }
378 | Log.v(LOG_TAG, "follow:"+followType+", hasAttach:"+hasRightAttachment+","+attachType);
379 | if (hasRightAttachment) {
380 | for (CardType type : cardTypes) {
381 | if (type instanceof Three && type.compareTo(followType)>0) {
382 | Three three = ((Three) type);
383 | //避免出现自己带自己的Bug
384 | if ( attachType!=null && three.getBodyList().get(0)
385 | .isSameValueAs(attachType.getCardList().get(0))){
386 | continue;
387 | }
388 | three.setAttachType(attachType);
389 | return three;
390 | }
391 | }
392 | }
393 | // 没有牌可以带,则找炸弹
394 | CardType bomb = findBombType(info, followType);
395 | if (bomb!=null){
396 | return bomb;
397 | }
398 | // 还是无牌,则不出
399 | return null;
400 | }
401 |
402 | //出牌前检查三条是否能带上单或对
403 | protected void optimizeThreeAttachments(Three three,
404 | ArrayList cardTypes) {
405 | for (CardType tp : cardTypes) {
406 | // XXX to be optimized: 2以上不带
407 | if (tp instanceof Single
408 | && tp.getCardList().get(0).getValue() < Card.CARD_VALUE_2) {
409 | three.setAttachType(tp);
410 | break;
411 | } else if (tp instanceof Pair
412 | && tp.getCardList().get(0).getValue() < Card.CARD_VALUE_2) {
413 | three.setAttachType(tp);
414 | break;
415 | }
416 | // 否则不带
417 | }
418 | }
419 |
420 | /**
421 | *
422 | * 按照指定的pattern尝试组合出目标牌列表。
423 | * 例如传入参数为[3,3,3],则将尝试找出三张点数为3的卡牌,并把这些按顺序加入一个列表然后返回。
424 | *
425 | * 注意:
426 | * - 该方法的实现假定list是升序排列的。
427 | *
428 | *
429 | * @param targetPattern 目标数值模式。
430 | * @param list 当前剩余卡牌列表。
431 | * @return 如果能找出这样的卡牌,则返回该列表;否则返回 null。
432 | */
433 | protected ArrayList takeoutCards(int[] targetPattern, ArrayList list){
434 | if (targetPattern==null || list==null){
435 | Log.w(LOG_TAG, "takecards return null due to null param.");
436 | return null;
437 | }
438 | int patternLength = targetPattern.length;
439 | int cardLength = list.size();
440 | if (patternLength > cardLength){
441 | Log.d(LOG_TAG, "takecards return null due to no enough length.");
442 | return null;
443 | }
444 | ArrayList targetList = new ArrayList<>();
445 | //创建临时复制数组,然后从中迭代遍历,如果匹配则加入目标列表,并从临时复制数组中删除迭代的元素。
446 | //最后检查结果,如果目标列表长度合适则返回,否则返回null。
447 | ArrayList internalTempList = new ArrayList<>(list);
448 | Iterator iterator = internalTempList.iterator();
449 | int matchIndex = 0;
450 | while (iterator.hasNext()){
451 | Card card = iterator.next();
452 | if (card.getValue()== targetPattern[matchIndex]){
453 | targetList.add(card);
454 | iterator.remove();
455 | matchIndex++;
456 | //如果已经到了要出牌的张数,则终止循环
457 | if (matchIndex == targetPattern.length){
458 | break;
459 | }
460 | }
461 | }
462 | //手动清除列表内容,方便GC
463 | internalTempList.clear();
464 | if (targetList.size() != targetPattern.length){
465 | targetList.clear();
466 | Log.v(LOG_TAG, "takecards: pattern="+Arrays.toString(targetPattern)+ ", not found");
467 | return null;
468 | }
469 | Log.d(LOG_TAG, "takecards: pattern="+Arrays.toString(targetPattern)+ ", res="+targetList.toString());
470 | return targetList;
471 | }
472 |
473 | /**
474 | * 将手牌按照一般原则进行组合。
475 | * @param list 手牌列表
476 | * @return 返回封装后的 PlayerCardsInfo 对象,其中的 cardTypes字段保证不为null且已进行过排序。
477 | */
478 | protected PlayerCardsInfo makeCards(final List list){
479 | if (list==null || list.size()==0){
480 | return null;
481 | }
482 | Log.d(LOG_TAG, "cards before make: "+list.toString());
483 | //复制一个列表以便内部操作,避免直接操纵玩家手牌。
484 | PlayerCardsInfo playerInfo = new PlayerCardsInfo();
485 | ArrayList cloneList= new ArrayList<>(list);
486 | Collections.sort(cloneList, Card.COMPARATOR_WITH_SUIT);
487 | //判断王炸是否存在。
488 | ArrayList rocket = takeoutCards(new int[] {
489 | Card.CARD_VALUE_JOKER_S, Card.CARD_VALUE_JOKER_B }, cloneList);
490 | if (rocket!=null){
491 | playerInfo.cardTypes.add(new Rocket(rocket));
492 | cloneList.removeAll(rocket);
493 | }
494 | //找出所有的炸弹。通常情况下,炸弹也是不会进行拆分处理的。
495 | //注意:这里使用lastNotFoundValue跳过相同点数的数值,避免无谓的循环。
496 | ArrayList bombs = new ArrayList<>();
497 | for (int i = 0, lastNotFoundValue=0, lastFoundValue=0; i < cloneList.size(); i++) {
498 | int cardValue = cloneList.get(i).getValue();
499 | if (lastNotFoundValue==cardValue || lastFoundValue==cardValue){
500 | continue ;
501 | }
502 | int[] bombPattern = new int[] { cardValue, cardValue, cardValue,
503 | cardValue };
504 | ArrayList bombCards = takeoutCards(bombPattern, cloneList);
505 | if (bombCards != null) {
506 | lastFoundValue = cardValue;
507 | bombs.add(new Bomb(bombCards));
508 | }
509 | else{
510 | lastNotFoundValue = cardValue;
511 | }
512 | }
513 | for (Bomb bomb: bombs){
514 | cloneList.removeAll(bomb.getCardList());
515 | playerInfo.cardTypes.add(bomb);
516 | }
517 | //找出所有的顺子
518 | List numbers = StraightAnalyst.getAllStraights(cloneList);
519 | ArrayList straights = new ArrayList<>();
520 | for (StraightNumbers num : numbers){
521 | ArrayList tempList = takeoutCards(num.asIntegerArray(), cloneList);
522 | if (tempList != null){
523 | Straight tempStr = new Straight(tempList);
524 | straights.add(tempStr);
525 | }
526 | }
527 | for (Straight str:straights){
528 | playerInfo.cardTypes.add(str);
529 | cloneList.removeAll(str.getCardList());
530 | }
531 |
532 | //找出所有的三条
533 | ArrayList threesWithNoAirplane = new ArrayList<>();
534 | for (int i = 0, lastNotFoundValue=0, lastFoundValue=0; i < cloneList.size(); i++) {
535 | int cardValue = cloneList.get(i).getValue();
536 | if (lastNotFoundValue==cardValue || lastFoundValue==cardValue){
537 | continue ;
538 | }
539 | int[] threePattern = new int[] { cardValue, cardValue, cardValue};
540 | ArrayList threes = takeoutCards(threePattern, cloneList);
541 | if (threes != null) {
542 | lastFoundValue = cardValue;
543 | threesWithNoAirplane.add(new Three(threes));
544 | }
545 | else{
546 | lastNotFoundValue = cardValue;
547 | }
548 | }
549 | for (Three thr: threesWithNoAirplane){
550 | cloneList.removeAll(thr.getCardList());
551 | }
552 | //优化为飞机后再加入三条列表
553 | if (threesWithNoAirplane.size()>2){
554 | ArrayList planes = makeAirplaneUsingThree(threesWithNoAirplane);
555 | for (Airplane air:planes){
556 | playerInfo.cardTypes.add(air);
557 | threesWithNoAirplane.removeAll(air.getBodyThrees());
558 | }
559 | }
560 | playerInfo.cardTypes.addAll(threesWithNoAirplane);
561 |
562 | //找出所有的对子
563 | ArrayList pairsSeparately = new ArrayList<>();
564 | for (int i = 0, lastFoundValue=0; i < cloneList.size(); i++) {
565 | int cardValue = cloneList.get(i).getValue();
566 | if (cardValue==lastFoundValue){
567 | continue;
568 | }
569 | int[] pairPattern = new int[] { cardValue, cardValue};
570 | ArrayList pairs = takeoutCards(pairPattern, cloneList);
571 | if (pairs != null) {
572 | lastFoundValue = cardValue;
573 | pairsSeparately.add(new Pair(pairs));
574 | }
575 | }
576 | for (Pair pair: pairsSeparately){
577 | cloneList.removeAll(pair.getCardList());
578 | }
579 | if (pairsSeparately.size()>2){
580 | ArrayList dbs = makeDoubleStraightUsingPairs(pairsSeparately);
581 | for (DoubleStraight db: dbs){
582 | playerInfo.cardTypes.add(db);
583 | }
584 | }
585 | playerInfo.cardTypes.addAll(pairsSeparately);
586 |
587 | //剩下的都是单牌
588 | Iterator singleIterator = cloneList.iterator();
589 | while (singleIterator.hasNext()){
590 | playerInfo.cardTypes.add(new Single(singleIterator.next()));
591 | singleIterator.remove();
592 | }
593 | Collections.sort(playerInfo.cardTypes, CardType.SORT_COMPARATOR);
594 | statPlayerCardsInfo(playerInfo);
595 | return playerInfo;
596 | }
597 |
598 | /**
599 | * 使用三条组装飞机。注意:如果能组成飞机,则所有组成飞机的三条都将被移除。
600 | */
601 | private ArrayList makeAirplaneUsingThree(ArrayList threes){
602 | int lastValue=0;
603 | ArrayList tempAirThreeRef = new ArrayList<>();
604 | ArrayList tempAirplanes = new ArrayList<>();
605 | ArrayList toBeRemoved = new ArrayList<>();
606 | for (Three tr : threes) {
607 | int thisValue = tr.getBodyList().get(0).getValue();
608 | //飞机不会包含2以上的数字
609 | if (thisValue>=Card.CARD_VALUE_2){
610 | break;
611 | }
612 | // 两个或更多三条可以组成飞机
613 | if (lastValue != 0 && (thisValue - lastValue != 1) && tempAirThreeRef.size() >= 2) {
614 | ArrayList cards = new ArrayList<>();
615 | for (Three airThr : tempAirThreeRef) {
616 | toBeRemoved.add(airThr);
617 | cards.addAll(airThr.getCardList());
618 | }
619 | tempAirplanes.add(new Airplane(cards));
620 | lastValue = 0;
621 | tempAirThreeRef.clear();
622 | }
623 | //否则,如果不连续则清空后加入,连续则直接加入
624 | else{
625 | if (lastValue!=0 && thisValue-lastValue!=1){
626 | tempAirThreeRef.clear();
627 | }
628 | lastValue = thisValue;
629 | tempAirThreeRef.add(tr);
630 | }
631 | }
632 | //循环结束进行最后的判断。由于过程中不连续的已经被剔除,所以这里一定是连续的。
633 | if (tempAirThreeRef.size() >= 2) {
634 | ArrayList cards = new ArrayList<>();
635 | for (Three airThr : tempAirThreeRef) {
636 | toBeRemoved.add(airThr);
637 | cards.addAll(airThr.getCardList());
638 | }
639 | tempAirplanes.add(new Airplane(cards));
640 | tempAirThreeRef.clear();
641 | }
642 | threes.removeAll(toBeRemoved);
643 | return tempAirplanes;
644 | }
645 |
646 | /**
647 | * 使用对子组装连对。注意:如果能组成连对,则所有组成连对的对子都将被移除。
648 | */
649 | private ArrayList makeDoubleStraightUsingPairs(
650 | ArrayList pairs) {
651 | int lastValue = 0;
652 | ArrayList tempPairRef = new ArrayList<>();
653 | ArrayList tempDoubles = new ArrayList<>();
654 | ArrayList toBeRemoved = new ArrayList<>();
655 | for (Pair tr : pairs) {
656 | int thisValue = tr.getCardList().get(0).getValue();
657 | //如果列表中的对子不再连续或遇到2,且能组成连对
658 | if (lastValue != 0
659 | && ((thisValue - lastValue != 1) || thisValue >= Card.CARD_VALUE_2)) {
660 | // 三个或更多对子可以组成连对
661 | if (tempPairRef.size() >= 3) {
662 | ArrayList cards = new ArrayList<>();
663 | for (Pair airThr : tempPairRef) {
664 | toBeRemoved.add(airThr);
665 | cards.addAll(airThr.getCardList());
666 | }
667 | tempDoubles.add(new DoubleStraight(cards));
668 | tempPairRef.clear();
669 | }
670 | }
671 | //否则,如果不连续则清空,连续则加入
672 | else if (lastValue!=0 && thisValue-lastValue!=1){
673 | tempPairRef.clear();
674 | }
675 | else{
676 | tempPairRef.add(tr);
677 | }
678 | // 无论是不是,都更新最新的值
679 | lastValue = thisValue;
680 | }
681 | //循环结束进行最后的判断。由于过程中不连续的已经被剔除,所以这里一定是连续的。
682 | if (tempPairRef.size() >= 3) {
683 | //判断其中装的是不是一样的
684 | ArrayList cards = new ArrayList<>();
685 | for (Pair airThr : tempPairRef) {
686 | toBeRemoved.add(airThr);
687 | cards.addAll(airThr.getCardList());
688 | }
689 | tempDoubles.add(new DoubleStraight(cards));
690 | tempPairRef.clear();
691 | }
692 | pairs.removeAll(toBeRemoved);
693 | return tempDoubles;
694 | }
695 |
696 | //判断是否需要跟牌
697 | private boolean needToFollow(CardType cardsToFollow){
698 | //如果自己是地主,则无需考虑,有牌必打
699 | if (bindPlayer.isLandlord()){
700 | return true;
701 | }
702 | //如果自己是地主的上家, 默认出牌
703 | if (bindPlayer.isPriorOfLandlord){
704 | CardType partnerCards = bindPlayer.getPriorPlayer().getLastCards();
705 | //搭档没有打,则跟牌
706 | if (partnerCards==null){
707 | return true;
708 | }
709 | //如果搭档跟了牌且牌比较大,则不跟牌
710 | if ((cardsToFollow instanceof Single
711 | && partnerCards.getCardList().get(0).getValue() >= Card.CARD_VALUE_A)
712 | || (cardsToFollow instanceof Pair
713 | && partnerCards.getCardList().get(0).getValue() >= Card.CARD_VALUE_K)) {
714 | return false;
715 | }
716 | //如果跟的牌是炸弹或顺子,不跟牌
717 | if (partnerCards instanceof BombType
718 | || partnerCards instanceof Straight) {
719 | return false;
720 | }
721 | return true;
722 | }
723 | //如果自己是地主的下家
724 | if (bindPlayer.isNextOfLandlord){
725 | CardType landCards = bindPlayer.getPriorPlayer().getLastCards();
726 | //如果地主没有出牌,则不出
727 | if (landCards==null){
728 | return false;
729 | }
730 | //如果是炸弹或顺子,有牌必跟
731 | if (landCards instanceof BombType || landCards instanceof Straight){
732 | return true;
733 | }
734 | CardType partnerCards = bindPlayer.getNextPlayer().getLastCards();
735 | //搭档上次没有跟牌,则打出
736 | if (partnerCards==null){
737 | return true;
738 | }
739 | //如果当前的牌是搭档打出,默认不打
740 | if (partnerCards == cardsToFollow) {
741 | //作为地主下家对卡牌的大小可以小一些
742 | if ((cardsToFollow instanceof Single && partnerCards
743 | .getCardList().get(0).getValue() < Card.CARD_VALUE_Q)
744 | || (cardsToFollow instanceof Pair && partnerCards
745 | .getCardList().get(0).getValue() < Card.CARD_VALUE_J)) {
746 | return true;
747 | }
748 | return false;
749 | }
750 | //如果有独立的无需拆牌就能打的,则打出
751 | ArrayList cardTypes = bindPlayer.cardsInfo.cardTypes;
752 | for (CardType type: cardTypes){
753 | if (type.getClass().equals(cardsToFollow)){
754 | return true;
755 | }
756 | }
757 | return false;
758 | }
759 | return false;
760 | }
761 |
762 | private void statPlayerCardsInfo(PlayerCardsInfo info){
763 | //force init
764 | info.hasRocket = false;
765 | info.bombCount = 0;
766 | info.pairCount = 0;
767 | info.singleCount = 0;
768 | info.threeCount = 0;
769 | info.straightCount=0;
770 |
771 | info.expectedRound = info.cardTypes.size();
772 |
773 | for (CardType type: info.cardTypes){
774 | if (type instanceof Rocket){
775 | info.hasRocket = true;
776 | }
777 | if (type instanceof BombType){
778 | info.bombCount++;
779 | }
780 | else if (type instanceof Straight){
781 | info.straightCount++;
782 | }
783 | else if (type instanceof Three){
784 | info.threeCount++;
785 | }
786 | else if (type instanceof Single){
787 | info.singleCount++;
788 | }
789 | else if (type instanceof Pair){
790 | info.pairCount++;
791 | }
792 | }
793 | }
794 |
795 | }
796 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/ai/Game.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.ai;
2 |
3 | import java.util.ArrayList;
4 |
5 | import com.mym.landlords.card.Card;
6 |
7 | /**
8 | * 代表一盘游戏的实体类,用于记录游戏内的各个变量。
9 | * @author Muyangmin
10 | * @create 2015-3-18
11 | */
12 | public final class Game {
13 |
14 | public static enum Status{
15 | /** 准备(发牌)阶段 */
16 | Preparing,
17 | /** 叫地主阶段 */
18 | CallingLandlord,
19 | /** 进行阶段 */
20 | Playing,
21 | /** 牌局结束展示AI手牌 */
22 | ShowingAICards,
23 | /** 游戏结束阶段 */
24 | Gameover;
25 | }
26 | /** 如取此值,表示不叫。 */
27 | public static final int BASIC_SCORE_NONE = 0;
28 | public static final int BASIC_SCORE_ONE = 1;
29 | public static final int BASIC_SCORE_TWO = 2;
30 | public static final int BASIC_SCORE_THREE = 3;
31 |
32 | /** 炸弹总数 */
33 | public int boomCount;
34 | /** 基础积分。 */
35 | public int basicScore;
36 | /** 当前状态 */
37 | public Status status;
38 | /** 地主玩家 */
39 | public Player landlordPlayer;
40 |
41 | public ArrayList playerLeftCards = new ArrayList<>(); //记牌器功能
42 | public ArrayList playerHumanCards = new ArrayList<>(); //记牌器功能
43 | public ArrayList playerRightCards = new ArrayList<>(); //记牌器功能
44 |
45 | private static Game instance = new Game();
46 |
47 | //隐藏构造方法
48 | private Game(){}
49 |
50 | /**
51 | * 开始一局新游戏。注意:前一局的所有记录将丢失。
52 | * @return 返回一个初始化后的游戏变量。
53 | */
54 | public static Game newGame(){
55 | //采用享元模式设计,始终使用同一个对象,降低内存消耗。
56 | instance.basicScore = 0;
57 | instance.boomCount =0;
58 | instance.status = Status.Preparing;
59 | instance.landlordPlayer = null;
60 | instance.playerHumanCards.clear();
61 | instance.playerLeftCards.clear();
62 | instance.playerRightCards.clear();
63 | return instance;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/ai/Player.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.ai;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | import android.util.Log;
8 |
9 | import com.mym.landlords.ai.Game.Status;
10 | import com.mym.landlords.card.Card;
11 | import com.mym.landlords.card.CardType;
12 |
13 | /**
14 | * 代表玩家的实体类。
15 | *
16 | * @author Muyangmin
17 | * @create 2015-3-18
18 | */
19 | public final class Player {
20 | private String playerName; //玩家名称
21 | private ArrayList handCards; // 手牌列表
22 | private boolean isLandlord; // 是否是地主
23 | private boolean isAiPlayer; // 是否是AI玩家
24 | private Player priorPlayer; // 上手玩家
25 | private Player nextPlayer; // 下手玩家
26 | private CardType lastCards; //出的最后一手牌,用于AI判断和逻辑控制
27 | private int calledScore = Integer.MIN_VALUE;// 叫的分数, Integer.MIN_VALUE表示未赋值
28 |
29 | /* 以下部分为 AI需要用到的属性 */
30 | private AI aiRobot; //机器AI
31 | protected PlayerCardsInfo cardsInfo; //卡牌分析结果
32 | /** 是否为地主的上家。 */
33 | protected boolean isPriorOfLandlord = false;
34 | /** 是否为地主的下家。 */
35 | protected boolean isNextOfLandlord = false;
36 |
37 | /**
38 | * 创建一个新的AI玩家实例。
39 | * @param name 玩家名称。
40 | */
41 | public static final Player newAiPlayer(String name) {
42 | Player player = new Player(true, name == null ? "" : name);
43 | player.aiRobot = new AI(player);
44 | return player;
45 | }
46 |
47 | /**
48 | * 创建一个非AI玩家实例。
49 | * @param name 玩家名称
50 | */
51 | public static final Player newHumanPlayer(String name) {
52 | return new Player(false, name);
53 | }
54 |
55 | /**
56 | * 重置Player的状态,包括:
57 | *
58 | * - 重置地主标志位;
59 | * - 清空手牌;
60 | * - 重置最后打出的牌和叫的地主分数等。
61 | *
62 | */
63 | public synchronized final void reset(){
64 | isLandlord = false;
65 | handCards.clear();
66 | lastCards = null;
67 | calledScore = Integer.MIN_VALUE;
68 | }
69 |
70 | private Player(boolean isAi, String name) {
71 | isAiPlayer = isAi;
72 | playerName = name;
73 | }
74 |
75 | public int getCalledScore() {
76 | return calledScore;
77 | }
78 |
79 | public ArrayList getHandCards() {
80 | return handCards;
81 | }
82 |
83 | public CardType getLastCards() {
84 | return lastCards;
85 | }
86 |
87 | /**
88 | * 打出卡牌。参数为null表示不出,但也将清空上一手牌的记录。
89 | * @param type 要打出的当前一手牌。
90 | */
91 | public final synchronized void giveOutCards(CardType type){
92 | lastCards = type;
93 | if (lastCards!=null){
94 | Log.d(playerName, "giveoutcard:"+type);
95 | handCards.removeAll(lastCards.getCardList());
96 | if (isAiPlayer){
97 | refreshCardsInfo();
98 | }
99 | }
100 | }
101 |
102 | public Player getNextPlayer() {
103 | return nextPlayer;
104 | }
105 |
106 | public Player getPriorPlayer() {
107 | return priorPlayer;
108 | }
109 |
110 | public boolean isAiPlayer() {
111 | return isAiPlayer;
112 | }
113 |
114 | public boolean isLandlord() {
115 | return isLandlord;
116 | }
117 |
118 | private final void checkAiPlayer(){
119 | if (!isAiPlayer){
120 | throw new RuntimeException("This method should not be invoked from a non-ai player.");
121 | }
122 | }
123 |
124 | /**
125 | * 执行叫地主操作。最终得出的分数要使用 {@link #getCalledScore()}方法获得。
126 | * @param minScore 地主最低分数(通常是本局前两位喊出来的)。
127 | */
128 | public synchronized final void callLandlord(int minScore){
129 | checkAiPlayer();
130 | calledScore = aiRobot.callLandlord(handCards, minScore >= 0 ? minScore : 0);
131 | }
132 |
133 | /**
134 | * 重新分析卡牌信息。
135 | */
136 | private final void refreshCardsInfo(){
137 | checkAiPlayer();
138 | if (cardsInfo!=null){
139 | cardsInfo.recycle();
140 | }
141 | cardsInfo = aiRobot.makeCards(handCards);
142 | Log.v(playerName, "Final playerInfo:"+cardsInfo);
143 | }
144 |
145 | /**
146 | * 该方法使玩家进行下一个回合。可能使用到的输入有当前的游戏状态、上一个玩家的操作等。
147 | *
148 | * @param currentGame
149 | * 当前正在进行的游戏实例。如果为null或处于游戏结束后的某个阶段,则会抛出异常。
150 | */
151 | public synchronized void nextRound(Game currentGame) {
152 | if (currentGame == null || currentGame.status == Status.ShowingAICards
153 | || currentGame.status == Status.Gameover) {
154 | throw new IllegalArgumentException("invalid game instance.");
155 | }
156 | }
157 |
158 | /**
159 | * 执行出牌或跟牌策略。
160 | * @param lastType 场上最后打出的卡牌牌型对象。当参数为null时表示自由出牌。
161 | * @return 返回一个合适的对象,该对象一定是逻辑合法(即大于参数牌型)的;或者为null。
162 | */
163 | public CardType followCards(CardType lastType){
164 | checkAiPlayer();
165 | return aiRobot.followCards(lastType);
166 | }
167 |
168 |
169 | /**
170 | * 设置手牌并自动排序。
171 | * @param handCards 手牌列表,不能为null。
172 | */
173 | public void setHandCards(List handCards) {
174 | if (handCards == null) {
175 | throw new RuntimeException("handCards cannot be null.");
176 | }
177 | this.handCards = new ArrayList<>(handCards.size());
178 | this.handCards.addAll(handCards);
179 | Collections.sort(this.handCards, Card.COMPARATOR_WITH_SUIT);
180 | if (isAiPlayer){
181 | refreshCardsInfo();
182 | }
183 | }
184 |
185 | /**
186 | * 设置为地主并将底牌加入手中。
187 | *
188 | * @param awardCards
189 | * 底牌列表,不能为null。
190 | */
191 | public void setLandlord(List awardCards) {
192 | if (awardCards == null) {
193 | throw new RuntimeException("awardCards cannot be null.");
194 | }
195 | this.isLandlord = true;
196 | getPriorPlayer().isPriorOfLandlord = true;
197 | getNextPlayer().isNextOfLandlord = true;
198 |
199 | //实现底牌加入手中时的选中状态
200 | for (Card card : this.handCards){
201 | card.setPicked(false);
202 | }
203 | for (Card card : awardCards){
204 | card.setPicked(true);
205 | this.handCards.add(card);
206 | }
207 | Collections.sort(this.handCards, Card.COMPARATOR_WITH_SUIT);
208 |
209 | //对于 AI,重新组合手牌
210 | if (isAiPlayer){
211 | refreshCardsInfo();
212 | }
213 |
214 | }
215 |
216 | /**
217 | * 设置座位。
218 | *
219 | * @param prior
220 | * 上家(左方)
221 | * @param next
222 | * 下家(右方)
223 | */
224 | public void setSeat(Player prior, Player next) {
225 | this.priorPlayer = prior;
226 | this.nextPlayer = next;
227 | }
228 |
229 | public void setCalledScore(int calledScore) {
230 | this.calledScore = calledScore;
231 | }
232 |
233 | public String getPlayerName() {
234 | return playerName;
235 | }
236 |
237 | @Override
238 | public String toString() {
239 | StringBuilder builder = new StringBuilder();
240 | builder.append("Player [").append(playerName)
241 | .append(isAiPlayer ? ", isAiPlayer." : "").append("]");
242 | return builder.toString();
243 | }
244 |
245 | }
246 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/ai/PlayerCardsInfo.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.ai;
2 |
3 | import java.util.ArrayList;
4 |
5 | import com.mym.landlords.card.CardType;
6 |
7 | /**
8 | * 存放玩家的手牌分析结果。
9 | * @author Muyangmin
10 | * @create 2015-3-26
11 | */
12 | public final class PlayerCardsInfo {
13 |
14 | //hide accessibility
15 | protected PlayerCardsInfo() {}
16 |
17 | /**单牌的张数。 */
18 | protected int singleCount = 0;
19 | /**独立的对子的个数。 */
20 | protected int pairCount = 0;
21 | /**三条的个数。 */
22 | protected int threeCount = 0;
23 | /**顺子的个数。 */
24 | protected int straightCount = 0;
25 | /**炸弹的个数(含王炸)。*/
26 | protected int bombCount = 0;
27 | /**2和王的个数。 */
28 | protected int twoAndJokerCount = 0;
29 | /**是否有王炸。 */
30 | protected boolean hasRocket = false;
31 | protected int expectedRound = 0; //预期几个回合能出完牌
32 | protected ArrayList cardTypes = new ArrayList<>();//牌型列表
33 |
34 | public int getSingleCount() {
35 | return singleCount;
36 | }
37 |
38 | public int getPairCount() {
39 | return pairCount;
40 | }
41 |
42 | public int getThreeCount() {
43 | return threeCount;
44 | }
45 |
46 | public int getBombCount() {
47 | return bombCount;
48 | }
49 |
50 | public int getTwoAndJokerCount() {
51 | return twoAndJokerCount;
52 | }
53 |
54 | public boolean isHasRocket() {
55 | return hasRocket;
56 | }
57 |
58 | public int getExpectedRound() {
59 | return expectedRound;
60 | }
61 |
62 | public ArrayList getCardTypes() {
63 | return cardTypes;
64 | }
65 |
66 | @Override
67 | public String toString() {
68 | StringBuilder builder = new StringBuilder();
69 | builder.append("PlayerCardsInfo [singleCount=").append(singleCount)
70 | .append(", pairCount=").append(pairCount)
71 | .append(", threeCount=").append(threeCount)
72 | .append(", straightCount=").append(straightCount)
73 | .append(", bombCount=").append(bombCount)
74 | .append(", twoAndJokerCount=").append(twoAndJokerCount)
75 | .append(", hasRocket=").append(hasRocket)
76 | .append(", expectedRound=").append(expectedRound)
77 | .append(", cardTypes=").append(cardTypes).append("]");
78 | return builder.toString();
79 | }
80 |
81 | protected final void recycle(){
82 | if (cardTypes!=null){
83 | cardTypes.clear();
84 | }
85 | cardTypes = null;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/ai/StraightAnalyst.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.ai;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.Comparator;
6 | import java.util.HashMap;
7 | import java.util.Iterator;
8 | import java.util.List;
9 |
10 | import android.util.Log;
11 |
12 | import com.mym.landlords.card.Card;
13 | import com.mym.landlords.card.DoubleStraight;
14 | import com.mym.landlords.card.Straight;
15 |
16 | //package access
17 | final class StraightAnalyst {
18 |
19 | private static final String LOG_TAG = "StraightAnalyst";
20 |
21 | /**
22 | * 找出所有能组成顺子的数值组合。
23 | * @param cardList 卡牌列表。
24 | * @return 顺子数值列表
25 | */
26 | protected static List getAllStraights(
27 | ArrayList cardList) {
28 | // Log.d(LOG_TAG, "original list:"+cardList.toString());
29 | //transfer int array
30 | ArrayList leftCardValues = new ArrayList<>();
31 | for (Card card: cardList){
32 | //过滤2和王
33 | if (card.getValue() < Card.CARD_VALUE_2){
34 | leftCardValues.add(card.getValue());
35 | }
36 | }
37 | // Log.d(LOG_TAG, "leftCardValue:" + leftCardValues.toString());
38 | List shortList = getShortestStraightList(leftCardValues);
39 | // Log.d(LOG_TAG, "shortest:"+shortList.toString());
40 | extendStraight(shortList, leftCardValues);
41 | // Log.d(LOG_TAG, "after extends:"+shortList.toString());
42 | concatPossibleStraights(shortList);
43 | Log.d(LOG_TAG, "final concat:"+shortList.toString());
44 | return shortList;
45 | }
46 |
47 | /**
48 | * 强行凑出顺子。
49 | * @param follow 要跟牌的顺子
50 | * @return 返回一个所有可能点数情况的列表,这些顺子的点数彼此可能有所重叠。如果无牌可出,则列表为空。
51 | */
52 | protected static ArrayList forceGetStraights(Straight follow,
53 | ArrayList cards) {
54 | if (follow==null || cards.size()< follow.length){
55 | return null;
56 | }
57 | ArrayList forceStraights = new ArrayList<>();
58 | Card lastCard = null;
59 | int size = cards.size();
60 | ArrayList tempCards = new ArrayList<>();
61 | for (int i=0; i=Card.CARD_VALUE_2){
67 | break;
68 | }
69 | //比较点数
70 | if (thisCard.compareTo(follow.getCardList().get(0)) <= 0){
71 | continue;
72 | }
73 | if (lastCard==null){
74 | tempCards.add(thisCard);
75 | lastCard = thisCard;
76 | continue;
77 | }
78 | int increment = thisCard.getValue()-lastCard.getValue();
79 | if (increment==0){
80 | continue;
81 | }
82 | else if (increment==1){
83 | tempCards.add(thisCard);
84 | lastCard = thisCard;
85 | if (tempCards.size()==follow.length){
86 | break;
87 | }
88 | }
89 | //大于1不会构成顺子。
90 | else {
91 | break;
92 | }
93 | }
94 | if (tempCards.size()==follow.length){
95 | Straight straight = new Straight(tempCards);
96 | if (straight.compareTo(follow) > 0){
97 | forceStraights.add(straight);
98 | }
99 | }
100 | }
101 | return forceStraights;
102 | }
103 |
104 | /**
105 | * 强行凑出双顺。
106 | * @param follow 要跟牌的双顺
107 | * @return 返回一个所有可能点数情况的列表,这些双顺的点数彼此可能有所重叠。如果无牌可出,则列表为空。
108 | */
109 | protected static ArrayList forceGetDoubleStraights(
110 | DoubleStraight follow, ArrayList cards) {
111 | if (follow==null || cards.size()< follow.length){
112 | return null;
113 | }
114 | ArrayList forceStraights = new ArrayList<>();
115 | int size = cards.size();
116 | ArrayList tempCards = new ArrayList<>();
117 | for (int i=0; i 0){
144 | forceStraights.add(straight);
145 | }
146 | }
147 | }
148 | return forceStraights;
149 | }
150 |
151 | /**
152 | * 获取所有的最短顺子,即所有的五连。
153 | * @param cardValues 剩余卡牌点数。
154 | * @return 返回匹配成功的所有五连顺子列表。
155 | */
156 | private static List getShortestStraightList(
157 | ArrayList cardValues) {
158 | List strList = new ArrayList<>();
159 | StraightNumbers straight;
160 | while ((straight = getSmallestShortList(cardValues)) != null) {
161 | cardValues.removeAll(straight.numbers);
162 | strList.add(straight);
163 | }
164 | return strList;
165 | }
166 |
167 | /**
168 | * 选取最小的五连,参数必须非递减有序。
169 | * @param values 剩余牌值
170 | * @return 一旦选取到5张组成的顺子则立即返回这5张牌组成的列表,否则返回null。
171 | */
172 | private static StraightNumbers getSmallestShortList(ArrayList values) {
173 | ArrayList integers = new ArrayList<>();
174 | for (Integer current : values) {
175 | if (integers.size() == 0) {
176 | integers.add(current);
177 | continue;
178 | }
179 | int increment = current - integers.get(integers.size() - 1);
180 | // 如果相等,不做理会
181 | if (increment == 0) {
182 | continue;
183 | }
184 | // 如果增量恰好为1,则加入列表
185 | else if (increment == 1) {
186 | integers.add(current);
187 | if (integers.size() == 5) {
188 | break;
189 | }
190 | }
191 | // 如果增量大于1则已经无法组成顺子,清空暂存列表
192 | else {
193 | integers.clear();
194 | continue;
195 | }
196 | }// end of loop for
197 | return integers.size() >= 5 ? new StraightNumbers(integers) : null;
198 | }
199 |
200 | // 扩展所有五连,以使能拼接的单牌都拼接上。
201 | private static void extendStraight(List strList,
202 | ArrayList leftCards) {
203 | Iterator strIter = strList.iterator();
204 | while (strIter.hasNext()) {
205 | StraightNumbers straight = strIter.next();
206 | if (leftCards.size()<=0){
207 | break ;
208 | }
209 | Iterator numIter = leftCards.iterator();
210 | while (numIter.hasNext()){
211 | Integer number = numIter.next();
212 | ArrayList list = straight.numbers;
213 | if (list.get(0)-number==1){
214 | list.add(0, number);
215 | numIter.remove();
216 | }
217 | else if (number - list.get(list.size()-1)==1){
218 | list.add(number);
219 | numIter.remove();
220 | }
221 | }
222 | } // end of while loop
223 | }
224 |
225 | // 连接扩展顺子,消除可以无缝连接的顺子。
226 | private static void concatPossibleStraights(List strList){
227 | //using HashMap instead of ArrayList to improve performance
228 | HashMap concatedMap = new HashMap<>(strList.size());
229 |
230 | Iterator strIterator = strList.iterator();//遍历迭代器
231 | final int listLength = strList.size();
232 | while (strIterator.hasNext()){
233 | ArrayList list = strIterator.next().numbers;
234 | if (list.size()<=0){
235 | continue;
236 | }
237 | //内层循环因为要随机访问所以不使用iterator迭代
238 | Integer max = list.get(list.size()-1);
239 | //因为顺子已经按照最小值有序排列,因此检测顺子的连接时只需要比较每一个的最小值和当前的最大值
240 | for (int i=0; i toRemoveList = new ArrayList<>(concatedMap.keySet());
259 | Collections.sort(toRemoveList, new Comparator() {
260 | @Override
261 | public int compare(Integer o1, Integer o2) {
262 | return o2.compareTo(o1); //reverse compare
263 | }
264 | });
265 | //注意:这里的遍历必须是int类型而不是 Integer类型,因为是记录的下标而不是元素自身的引用。
266 | for (int key : toRemoveList){
267 | strList.remove(key);
268 | }
269 | return ;
270 | }
271 | }
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/ai/StraightNumbers.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.ai;
2 |
3 | import java.util.ArrayList;
4 |
5 | //用于隐藏数据细节,避免复杂数据结构
6 | final class StraightNumbers{
7 | /** 代表能组成顺子的某个数字序列。其内容为升序排列。 */
8 | public final ArrayList numbers;
9 | private int[] intNumbers;
10 | //package access
11 | StraightNumbers(ArrayList list){
12 | this.numbers = list;
13 | }
14 | /**
15 | * 将 {@link #numbers}转换为一个数组并返回。
16 | * @return 返回一个数组,其顺序和遍历 {@link #numbers}的顺序一样。
17 | */
18 | public final int[] asIntegerArray(){
19 | if (intNumbers==null){
20 | int size = numbers.size();
21 | intNumbers = new int[size];
22 | for (int i=0; i getTips(CardType followType, List handCards){
33 | //如果是第一个出牌
34 | if (followType==null){
35 | PlayerCardsInfo info = robot.makeCards(handCards);
36 | ArrayList cardTypes = info.cardTypes;
37 | //优化带牌
38 | for (CardType type: cardTypes){
39 | if (type instanceof Three){
40 | robot.optimizeThreeAttachments((Three)type, info.cardTypes);
41 | }
42 | }
43 | return cardTypes;
44 | }
45 | //否则只能强制找出所有能组合出的适合当前牌型的牌
46 | ArrayList types = new ArrayList<>();
47 | ArrayList cloneList = new ArrayList<>(handCards);
48 | //王炸无法跟牌,直接返回空即可。
49 | if (followType instanceof Rocket){
50 | return types;
51 | }
52 | Card[] tempBombCards = new Card[4];
53 | final boolean followBomb = followType instanceof Bomb;
54 | final int bombValue = followType.getCardList().get(0).getValue();
55 | //先找出炸弹
56 | for (int i=0,lastValue=0; i cards =new ArrayList<>(Arrays.asList(tempBombCards));
79 | types.add(new Bomb(cards));
80 | cloneList.removeAll(cards);
81 | }
82 | }
83 | //如果是单牌,则找出所有比目标大的就可以了。
84 | if (followType instanceof Single){
85 | Card before = followType.getCardList().get(0);
86 | for (Card card: cloneList){
87 | if (card.compareTo(before) > 0){
88 | types.add(new Single(card));
89 | }
90 | }
91 | }
92 | //王炸可以拆开打单牌,但是对于其他情形就只能用作炸弹了
93 | int[] rocketPattern = new int[]{Card.CARD_VALUE_JOKER_S, Card.CARD_VALUE_JOKER_B};
94 | ArrayList rocketCards = robot.takeoutCards(rocketPattern, cloneList);
95 | if (rocketCards!=null){
96 | types.add(new Rocket(rocketCards));
97 | cloneList.removeAll(rocketCards);
98 | }
99 | //继续在剩下的卡片中寻找匹配的牌型
100 | if (followType instanceof Pair){
101 | //跳过比原牌小的
102 | Card before = followType.getCardList().get(0);
103 | for (int i=0, lastValue = 0; i+1 straights = StraightAnalyst.forceGetStraights(
142 | (Straight) followType, cloneList);
143 | if (straights!=null){
144 | types.addAll(straights);
145 | }
146 | }
147 | else if (followType instanceof DoubleStraight){
148 | ArrayList straights = StraightAnalyst.forceGetDoubleStraights(
149 | (DoubleStraight) followType, cloneList);
150 | if (straights!=null){
151 | types.addAll(straights);
152 | }
153 | }
154 | Collections.sort(types, CardType.SORT_COMPARATOR);
155 | return types;
156 | }
157 |
158 | /**
159 | * 创建一个包含指定元素的数组列表并返回。
160 | * @param params 要包含的元素。所有元素必须为同一类型,否则返回的列表的行为是不确定的(堆污染)。
161 | * @return 返回包含指定元素的列表,不会为null。
162 | */
163 | @SafeVarargs
164 | public static final ArrayList createArrayList(T... params){
165 | ArrayList list = new ArrayList<>();
166 | for (T object: params){
167 | list.add(object);
168 | }
169 | return list;
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/Airplane.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.Iterator;
6 |
7 |
8 | /**
9 | * @author Muyangmin
10 | * @create 2015-4-16
11 | */
12 | public class Airplane extends CardType implements NonBombType {
13 |
14 | private ArrayList bodyThrees;
15 |
16 | public Airplane(ArrayList list) {
17 | try {
18 | bodyThrees = getThreeLists(list);
19 | cardList = list;
20 | } catch (IllegalArgumentException e) {
21 | e.printStackTrace();
22 | throw new IllegalArgumentException("cannot makeup airplane: "+e.getMessage());
23 | }
24 | }
25 |
26 | private ArrayList getThreeLists(ArrayList list) throws IllegalArgumentException{
27 | if (list==null || list.isEmpty()){
28 | throw new IllegalArgumentException("param is null.");
29 | }
30 | //记录每张卡的点数对应的卡牌张数
31 | HashMap cardCountMap = new HashMap<>();
32 | //与 cardCountMap 同步更新的一个映射,保存相同点数的卡牌,以备后用。
33 | HashMap> cardMap = new HashMap<>();
34 | int three=0, two=0, one=0; //记录卡组数目
35 | for (Card card: list){
36 | int value = card.getValue();
37 | Integer already = cardCountMap.get(value);
38 | if (already==null){
39 | already = 0;
40 | }
41 | already++;
42 | if (already > 3){//超过四张一样的牌,不允许出牌。
43 | throw new IllegalArgumentException("too many same cards.");
44 | }
45 | //更新卡牌映射
46 | ArrayList sameCards = cardMap.get(value);
47 | if (sameCards==null){
48 | sameCards = new ArrayList<>();
49 | }
50 | sameCards.add(card);
51 | cardMap.put(value, sameCards);
52 | //更新计数
53 | cardCountMap.put(value, already);
54 | }
55 | for (ArrayList cards: cardMap.values()){
56 | switch (cards.size()){
57 | case 3: three++;break;
58 | case 2: two++; break;
59 | case 1: one++; break;
60 | }
61 | }
62 | if ( (one!=0 && two!=0) ){
63 | throw new IllegalArgumentException("mixed attachment, one="+one+",two="+two);
64 | }
65 | if ( (one!=0 && three!=one) || (two!=0 && three != two) ){
66 | throw new IllegalArgumentException("body and attachment not match."
67 | + "one=" + one + ",two=" + two + ",three=" + three);
68 | }
69 | //组装三条并带牌
70 | Iterator iterator = cardCountMap.keySet().iterator();
71 | ArrayList> bodyLists = new ArrayList<>();
72 | ArrayList> attachLists = new ArrayList<>();
73 | while (iterator.hasNext()){
74 | int value = iterator.next();
75 | if (cardCountMap.get(value)==3){ //是三条
76 | bodyLists.add(cardMap.get(value));
77 | }
78 | else{
79 | attachLists.add(cardMap.get(value));
80 | }
81 | }
82 | //bodyLists & attachLists must be same size.
83 | int size = bodyLists.size();
84 | //补充不带牌的情况
85 | if (attachLists.size() tempThrees = new ArrayList<>();
92 | for (int i=0; i getBodyThrees() {
99 | return bodyThrees;
100 | }
101 |
102 | @Override
103 | public String toString() {
104 | StringBuilder builder = new StringBuilder();
105 | builder.append("Airplane ").append(cardList);
106 | return builder.toString();
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/Bomb.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | import java.util.ArrayList;
4 |
5 | public final class Bomb extends CardType implements BombType{
6 |
7 | public Bomb(ArrayList list){
8 | if (list==null || list.size()!=4 || (!list.get(0).isSameValueAs(list.get(1)))
9 | || (!list.get(1).isSameValueAs(list.get(2)))
10 | || (!list.get(2).isSameValueAs(list.get(3)))){
11 | throw new IllegalArgumentException("this type must be 4 same cards!" + list.toString());
12 | }
13 | //保护性复制
14 | cardList = new ArrayList<>(list);
15 | }
16 |
17 | @Override
18 | public int compareTo(CardType another){
19 | int superCompare = super.compareTo(another);
20 | if (superCompare != UNDEFINED_COMPARE){
21 | return superCompare;
22 | }
23 | if (another instanceof Rocket){
24 | return -1;
25 | }
26 | if ( !(another instanceof Bomb) ){
27 | throw new ClassCastException("compare to wrong type:" + another.getClass().getSimpleName());
28 | }
29 | return cardList.get(0).compareTo(((Bomb)another).cardList.get(0));
30 | }
31 |
32 | @Override
33 | public String toString() {
34 | StringBuilder builder = new StringBuilder();
35 | builder.append("Bomb ").append(cardList);
36 | return builder.toString();
37 | }
38 | }
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/BombType.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | /**标记接口, 表示炸弹类型。 */
4 | public interface BombType {
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/Card.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | import java.io.Serializable;
4 | import java.util.Comparator;
5 |
6 | /**
7 | * 单张卡牌实体类。
8 | *
9 | *
卡牌的比较
10 | * 一般而言,在实现卡牌逻辑时只需要关心其大小;而在需要排序时往往还需要按照花色进行排序。
11 | * 因此,在排序时使用 {@link #COMPARATOR_WITH_SUIT},而在仅需要比较大小时请使用 {@link #compareTo(Card)}。
12 | *
13 | * @author Muyangmin
14 | * @create 2015-3-14
15 | */
16 | public final class Card implements Serializable, Comparable, Pickable{
17 | //-------------- 卡牌数值大小定义 BEGIN --------
18 | //NOTE:这里将所有卡牌按单打的大小依次+1,便于在构造器中检查参数合法性。
19 | public static final int CARD_VALUE_3 = 3;
20 | public static final int CARD_VALUE_4 = 4;
21 | public static final int CARD_VALUE_5 = 5;
22 | public static final int CARD_VALUE_6 = 6;
23 | public static final int CARD_VALUE_7 = 7;
24 | public static final int CARD_VALUE_8 = 8;
25 | public static final int CARD_VALUE_9 = 9;
26 | public static final int CARD_VALUE_10 = 10;
27 | public static final int CARD_VALUE_J = 11;
28 | public static final int CARD_VALUE_Q = 12;
29 | public static final int CARD_VALUE_K = 13;
30 | public static final int CARD_VALUE_A = 14;
31 | public static final int CARD_VALUE_2 = 15;
32 | /** 小王 */
33 | public static final int CARD_VALUE_JOKER_S = 16;
34 | /** 大王 */
35 | public static final int CARD_VALUE_JOKER_B = 17;
36 | //-------------- 卡牌数值大小定义 END --------
37 |
38 | private static final long serialVersionUID = 1L;
39 |
40 | private CardSuit suit; //卡牌花色
41 | private int value; //卡牌数值。即上面的大小值之一。
42 | private String valueStr; //卡牌数值的字符描述,主要用于提示或日志输出
43 | private boolean isPicked; // 该卡牌是否被选中。
44 |
45 | /**
46 | * 按卡牌花色和点数排序的Comparator。
47 | */
48 | public static final Comparator COMPARATOR_WITH_SUIT = new Comparator() {
49 |
50 | @Override
51 | public int compare(Card lhs, Card rhs) {
52 | // 先比较大小
53 | int compareResult = Integer.valueOf(lhs.value).compareTo(rhs.value);
54 | if (compareResult == 0) {
55 | // 如果大小相同则判断花色,保证手中所有大小相同的牌都按花色有序
56 | compareResult = Integer.valueOf(lhs.suit.ordinal()).compareTo(
57 | rhs.suit.ordinal());
58 | }
59 | return compareResult;
60 | }
61 |
62 | };
63 |
64 | /**
65 | * 创建一张新的卡牌。
66 | * @param suit 卡牌的花色。
67 | * @param value 卡牌的面值,必须为 {@value #CARD_VALUE_3} ~ {@value #CARD_VALUE_JOKER_B}之间的值。
68 | */
69 | public Card(CardSuit suit, int value) {
70 | if (suit == null || value < CARD_VALUE_3 || value > CARD_VALUE_JOKER_B) {
71 | throw new IllegalArgumentException("invalid card!suit="+suit+", value="+value);
72 | }
73 | this.suit = suit;
74 | this.value = value;
75 | this.valueStr = getValueLiteral(value);
76 | }
77 |
78 | // public int compareToXS(Card another) {
79 | // //先比较大小
80 | // int compareResult = Integer.valueOf(value).compareTo(another.value);
81 | // if (compareResult==0){
82 | // //如果大小相同则判断花色,保证手中所有大小相同的牌都按花色有序
83 | // compareResult = Integer.valueOf(suit.ordinal()).compareTo(another.suit.ordinal());
84 | // }
85 | // return compareResult;
86 | // }
87 |
88 | /**
89 | * 比较两张牌的点数大小,忽略花色。注意:不建议使用该方法比较大小王。
90 | */
91 | @Override
92 | public final int compareTo(Card another){
93 | return Integer.valueOf(value).compareTo(another.value);
94 | }
95 |
96 | /**
97 | * 比较两张牌的点数是否相同,该方法比 {@link #compareTo(Card)}方法更高效。
98 | * 注意:不建议使用该方法比较大小王。
99 | */
100 | public final boolean isSameValueAs(Card another){
101 | return value==another.value;
102 | }
103 |
104 | @Override
105 | public boolean isPicked() {
106 | return isPicked;
107 | }
108 |
109 | @Override
110 | public void setPicked(boolean isPicked) {
111 | this.isPicked = isPicked;
112 | }
113 |
114 |
115 | private final String getValueLiteral(int value){
116 | String str;
117 | switch (value) {
118 | case CARD_VALUE_3:
119 | case CARD_VALUE_4:
120 | case CARD_VALUE_5:
121 | case CARD_VALUE_6:
122 | case CARD_VALUE_7:
123 | case CARD_VALUE_8:
124 | case CARD_VALUE_9:
125 | case CARD_VALUE_10:
126 | str =String.valueOf(value);
127 | break;
128 | case CARD_VALUE_2:
129 | str = "2";
130 | break;
131 | case CARD_VALUE_J:
132 | str = "J";
133 | break;
134 | case CARD_VALUE_Q:
135 | str = "Q";
136 | break;
137 | case CARD_VALUE_K:
138 | str = "K";
139 | break;
140 | case CARD_VALUE_A:
141 | str = "A";
142 | break;
143 | case CARD_VALUE_JOKER_S:
144 | str = "S";
145 | break;
146 | case CARD_VALUE_JOKER_B:
147 | str = "B";
148 | break;
149 | default:
150 | throw new RuntimeException("Invalid default branch exec, cardvalue="+value);
151 | }
152 | return str;
153 | }
154 |
155 | @Override
156 | public String toString() {
157 | StringBuilder builder = new StringBuilder();
158 | //仅输出类似 "SPADE A"之类文本。
159 | builder./*append("Card [").*/append(suit).append(" ").append(valueStr)/*.append("]")*/;
160 | return builder.toString();
161 | }
162 |
163 | public CardSuit getSuit() {
164 | return suit;
165 | }
166 |
167 | public int getValue() {
168 | return value;
169 | }
170 | }
171 | /**
172 | * 实现逻辑上的可选择接口。
173 | * @author Muyangmin
174 | * @create 2015-3-24
175 | */
176 | interface Pickable{
177 | public boolean isPicked();
178 | public void setPicked(boolean isPicked);
179 | }
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/CardFactory.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * 卡牌工厂类,负责卡牌的印制\(^o^)/。
7 | * @author Muyangmin
8 | * @create 2015-3-14
9 | */
10 | public final class CardFactory {
11 |
12 | /**
13 | * 获得一副新牌。
14 | * @return 返回的卡牌按花色排列,大小王在最后。
15 | */
16 | public static ArrayList newCardPack(){
17 | ArrayList cards = new ArrayList<>();
18 | for (int i=Card.CARD_VALUE_3; i<=Card.CARD_VALUE_2; i++){
19 | cards.add(new Card(CardSuit.Spade, i));
20 | }
21 | for (int i=Card.CARD_VALUE_3; i<=Card.CARD_VALUE_2; i++){
22 | cards.add(new Card(CardSuit.Heart, i));
23 | }
24 | for (int i=Card.CARD_VALUE_3; i<=Card.CARD_VALUE_2; i++){
25 | cards.add(new Card(CardSuit.Club, i));
26 | }
27 | for (int i=Card.CARD_VALUE_3; i<=Card.CARD_VALUE_2; i++){
28 | cards.add(new Card(CardSuit.Diamond, i));
29 | }
30 | cards.add(new Card(CardSuit.Joker, Card.CARD_VALUE_JOKER_S));
31 | cards.add(new Card(CardSuit.Joker, Card.CARD_VALUE_JOKER_B));
32 | return cards;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/CardSuit.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | /**
4 | * 枚举卡牌的花色。
5 | * @author Muyangmin
6 | * @create 2015-3-14
7 | */
8 | public enum CardSuit {
9 | /**
10 | * 黑桃,A~K,13张.
11 | */
12 | Spade,
13 | /**
14 | * 红桃,A~K,13张。
15 | */
16 | Heart,
17 | /**
18 | * 梅花,A~K,13张。
19 | */
20 | Club,
21 | /**
22 | * 方片,A~K,13张。
23 | */
24 | Diamond,
25 | /**
26 | * 小王和大王。
27 | */
28 | Joker;
29 | }
30 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/CardType.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | import java.lang.reflect.InvocationTargetException;
4 | import java.util.ArrayList;
5 | import java.util.Comparator;
6 |
7 | import android.util.Log;
8 |
9 | /**
10 | * 牌型的抽象父类。每个该类及其子类的实例都代表一手符合牌型规则的牌。所有的牌型都必须实现 {@link BombType}和
11 | * {@link NonBombType}其中的一个且仅能实现一个。
12 | *
13 | *
获取卡牌列表
14 | * 为程序统一起见,将所有的牌型都视为一个卡牌的列表,这个列表存储在 {@link #cardList} 字段中,客户端代码通过
15 | * {@link #getCardList()}方式获得。这其中只有单牌是例外,因此只需将单牌处理为仅有一个元素的列表即可。
16 | *
17 | *
18 | *
从卡牌列表创建牌型
19 | * 在与用户交互的过程中,程序可能需要把用户的卡牌列表组合成牌型对象。{@link #createObjectFromCards(ArrayList)}方法
20 | * 可以完成这项工作。但是,子类必须有一个构造器仅带有ArrayList<Card>参数。
21 | *
22 | *
23 | *
牌型的排序
24 | * CardType类自身实现了 {@link Comparable}接口,{@link #SORT_COMPARATOR}实现了
25 | * {@link Comparator}接口。
26 | * 当需要对两手牌进行对比(例如出牌阶段下家和上家手牌大小的判断)时,使用 {@link CardType#compareTo(CardType)}方法;
27 | * 需要对不同手牌进行排序时(例如,出牌时总是希望先从包含最小卡牌的牌型开始),使用 {@link #SORT_COMPARATOR}进行排序。
28 | *
29 | *
30 | * @author Muyangmin
31 | * @create 2015-3-23
32 | */
33 | public abstract class CardType implements Comparable {
34 |
35 | /**
36 | *
37 | *
保护性复制
38 | * 子类在实现 带有list参数的构造器时应当使用保护性复制。这是因为用作参数的列表可能是临时的,一旦在外部执行了clear操作,
39 | * 则会造成列表为空的后果。
40 | *
41 | */
42 | /*
43 | * 参考代码:
44 | * ArrayList tempList = new ArrayList(2);
45 | * tempList.add(...); //add 2 elements
46 | * CardType p = new Pair(tempList); //here p.getCardList().size()=2
47 | * tempList.clear(); //here p.getCardList().size()=0 !
48 | */
49 | public CardType() {
50 | if (!(this instanceof BombType || this instanceof NonBombType)) {
51 | throw new RuntimeException(
52 | "Subclasses of CardType must implement either BombType or NonBombType!");
53 | }
54 | if (this instanceof BombType && this instanceof NonBombType) {
55 | throw new RuntimeException(
56 | "Subclasses of CardType must implement either BombType or NonBombType, not both!");
57 | }
58 | }
59 |
60 | /**
61 | * 用于实现所有牌型之间的排序。
62 | * @see CardType#getMinCard()
63 | */
64 | public static final Comparator SORT_COMPARATOR = new Comparator() {
65 |
66 | @Override
67 | public int compare(CardType lhs, CardType rhs) {
68 | Card left = lhs.getMinCard();
69 | Card right = rhs.getMinCard();
70 | if (left == null || right==null){
71 | throw new RuntimeException("subclass must implement getMinCard() method properly."
72 | + "leftclass=" + lhs.getClass().getSimpleName()
73 | + "rightclass=" + right.getClass().getSimpleName());
74 | }
75 | //由于大小王的点数本身比其他牌都要大,因此免去对王炸的判断,仅令炸弹牌大于非炸弹牌即可。
76 | if (lhs instanceof Bomb && (!(rhs instanceof Bomb))){
77 | return 1;
78 | }
79 | else if( (rhs instanceof Bomb) && (!(lhs instanceof Bomb)) ){
80 | return -1;
81 | }
82 | //按照牌的最小点数排序
83 | return left.compareTo(right);
84 | }
85 | };
86 |
87 | /**
88 | * 用于表示结果是未定义的、不明确的。
89 | */
90 | protected static final int UNDEFINED_COMPARE = -100;
91 |
92 | /**
93 | * 该种牌型的卡牌列表。子类必须在构造函数中为之赋值。
94 | */
95 | protected ArrayList cardList = null;
96 |
97 | /**
98 | * 获取该牌型中数值最小的卡牌,供 {@link #SORT_COMPARATOR} 排序时使用。
99 | */
100 | protected final Card getMinCard(){
101 | //assume list is sorted before this call
102 | // Collections.sort(cardList);
103 | //using getter method instead of field to check null pointer
104 | ArrayList list = getCardList();
105 | if (list.isEmpty()){
106 | Log.i("", "card is empty."+this.toString());
107 | }
108 | return getCardList().get(0);
109 | };
110 |
111 |
112 | /**
113 | * 实现牌型对象之间的对比。由于斗地主游戏不允许下家的点数和上家的点数相同,因此忽略花色。
114 | * 用于比较的两个对象要么至少一个是炸弹或王炸,要么必须是相同类型,否则将会抛出异常。
115 | * @param another
116 | * 需要进行比较的另一个对象
117 | * @return 实现炸弹类型和非炸弹类型的基本比对,该方法认为炸弹始终比另一个大。
118 | * 当且仅当其中一个是炸弹时会返回compareTo方法约定的值,
119 | * 如果两个都是炸弹或都不是炸弹, 则返回 {@link #UNDEFINED_COMPARE}。
120 | * @throws ClassCastException 如果两种牌型无法判断大小又无法转换时将抛出该异常。
121 | */
122 | @Override
123 | public int compareTo(CardType another) {
124 | //this super implementation just verify bomb type & class.
125 | //concrete value compare must be specified in subclass implementation.
126 | if (this instanceof NonBombType && another instanceof BombType) {
127 | return -1;
128 | } else if (this instanceof BombType && another instanceof NonBombType) {
129 | return 1;
130 | } else {
131 | //after bomb type check:
132 | Class> thisClz = getClass();
133 | Class> anotherClz = another.getClass();
134 | if (!thisClz.equals(anotherClz)) {
135 | throw new ClassCastException(
136 | "compareTo wrong type, this=" + thisClz.getSimpleName()
137 | + ", another=" + anotherClz.getSimpleName());
138 | }
139 | return UNDEFINED_COMPARE;
140 | }
141 | }
142 |
143 | /**
144 | * 获得这一手牌的卡牌列表。
145 | * @return 卡牌列表,不会为null(如果子类没有通过 {@link #cardList}
146 | * 初始化列表值,则会抛出NullPointerException)。
147 | */
148 | public final ArrayList getCardList() {
149 | if (cardList==null){
150 | throw new NullPointerException("subclass "
151 | + getClass().getSimpleName()
152 | + " must init cardList in constructor!");
153 | }
154 | return cardList;
155 | }
156 |
157 | /**
158 | * 判断当前牌能否打前一手牌。
159 | * @param before 前一手牌
160 | * @return 如果能打出则返回true,否则返回false。
161 | */
162 | public final boolean canAgainstType(CardType before) {
163 | if (before==null){
164 | return true;
165 | }
166 | if (this instanceof Rocket) {
167 | return true;
168 | }
169 | if (this instanceof BombType && before instanceof NonBombType) {
170 | return true;
171 | }
172 | //否则需要类型相同且大于参数值。
173 | return getClass().equals(before.getClass())
174 | && (getCardList().size()==before.getCardList().size())
175 | && (this.compareTo(before) > 0);
176 | }
177 | //
178 | // /**
179 | // * 用于标记当前的两个对象是否是完全一致的子类型,(例如,长度为5的顺子和长度为9的顺子并不完全一样)。
180 | // * @return 如果两个对象的子类型一样(即逻辑上可以在同一轮中由不同玩家打出),则返回true。
181 | // */
182 | // protected boolean isSameConcreteSubclass(CardType another){
183 | // return true;
184 | // }
185 |
186 | /**
187 | * 简单的工厂方法,用于把特定的卡牌列表转换为一个合适的类型。
188 | * @param cards 卡牌列表
189 | * @return 如果能把指定的卡牌转换为一个特定的类型,则返回这个类型的对象,否则返回null。
190 | */
191 | public static final CardType createObjectFromCards(ArrayList cards){
192 | CardType instance = null;
193 | int cardCount;
194 | if (cards==null || ((cardCount=cards.size())==0) ){
195 | return null;
196 | }
197 | if (cardCount==1){
198 | //仅有一种情况,因此直接尝试创建对象并返回。为符合单一出口原则,并不在这里直接return
199 | instance=createObject(cards, Single.class);
200 | }
201 | else if (cardCount==2){
202 | //只有对子和王炸两种情况
203 | if ((instance=createObject(cards, Pair.class))==null){
204 | instance = createObject(cards, Rocket.class);
205 | }
206 | }
207 | else if (cardCount==3) {
208 | instance = createObject(cards, Three.class);
209 | }
210 | else if (cardCount==4) {
211 | instance = createObject(cards, Bomb.class);
212 | //三带一
213 | if (instance==null){
214 | instance = createObject(cards, Three.class);
215 | }
216 | }
217 | else if (cardCount==5){
218 | instance = createObject(cards, Straight.class);
219 | //三带对
220 | if (instance==null){
221 | instance = createObject(cards, Three.class);
222 | }
223 | }
224 | else if (cardCount>=5){
225 | if (cardCount %2 ==0){
226 | instance = createObject(cards, DoubleStraight.class);
227 | }
228 | if (instance==null){
229 | instance = createObject(cards, Straight.class);
230 | }
231 | if (instance==null){
232 | instance = createObject(cards, Airplane.class);
233 | }
234 | }
235 | return instance;
236 | }
237 |
238 | //创建子类对象。该方法假定子类均有一个仅带有一个ArrayList参数的构造器。
239 | private static final CardType createObject(ArrayList cards,
240 | Class extends CardType> clz) {
241 | try {
242 | CardType instance = clz.getConstructor(ArrayList.class).newInstance(cards);
243 | return instance;
244 | }catch (InvocationTargetException e) {
245 | //ignore
246 | } catch (IllegalArgumentException e) {
247 | e.printStackTrace();
248 | } catch (InstantiationException e) {
249 | e.printStackTrace();
250 | } catch (IllegalAccessException e) {
251 | e.printStackTrace();
252 | } catch (NoSuchMethodException e) {
253 | e.printStackTrace();
254 | }
255 | return null;
256 | }
257 | }
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/DoubleStraight.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 |
6 | import com.mym.util.LangUtils;
7 |
8 | /**
9 | * 双顺(连对)牌型。
10 | *
11 | * @author Muyangmin
12 | * @create 2015-4-14
13 | */
14 | public class DoubleStraight extends CardType implements NonBombType {
15 |
16 | private ArrayList pairs; //对子的列表
17 | private final int startValue; //最小的对子的数值
18 | /**
19 | * 这个双顺所包含的对子数目。
20 | */
21 | public final int length;
22 |
23 | public DoubleStraight(ArrayList list) {
24 | pairs = dividePairs(list);
25 | if (pairs == null) {
26 | throw new IllegalArgumentException(
27 | "cannot make up double straight:" + list.toString());
28 | }
29 | //保护性复制
30 | cardList = new ArrayList<>(list);
31 | length = pairs.size();
32 | startValue = cardList.get(0).getValue();
33 | }
34 |
35 | // 分析连对,如果能组成连对,则返回对子的顺序列表,否则返回null。
36 | private ArrayList dividePairs(ArrayList list) {
37 | if (list == null || list.isEmpty()) {
38 | return null;
39 | }
40 | int listSize = list.size();
41 | if (listSize<6 || listSize%2==1){
42 | return null;
43 | }
44 | Collections.sort(list, Card.COMPARATOR_WITH_SUIT);
45 | ArrayList pairs = new ArrayList<>();
46 | for (int i = 0, lastValue=0; i < listSize; i += 2) {
47 | Card card1 = list.get(i);
48 | Card card2 = list.get(i + 1);
49 | if (card1.isSameValueAs(card2) //两张一样大
50 | && card1.getValue() getPairs() {
84 | return pairs;
85 | }
86 |
87 | @Override
88 | public String toString() {
89 | StringBuilder builder = new StringBuilder();
90 | builder.append("DoubleStraight [startValue=").append(startValue).append(", length=")
91 | .append(length).append(", cardList=").append(cardList)
92 | .append("]");
93 | return builder.toString();
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/HandCard.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 | //
3 | ///**
4 | // * 代表玩家手中的手牌。
5 | // *
6 | // * 从客观现实而言,纸牌和手牌在材料上并无区别;但从程序流程来讲,卡牌本身是无属性的,加入手牌后会有选中状态等属性。
7 | // * 并且每局游戏完成后,卡牌并不会重新印刷,而是利用原来的卡牌,但是手牌的这些属性是每次必须重新初始化的。
8 | // *
9 | // *
10 | // * 从面向对象设计方法而言,Card类作为单纯的物体实体类,不应该掺杂业务和逻辑方法。而HandCard类就不是如此。
11 | // *
12 | // *
13 | // * @author Muyangmin
14 | // * @create 2015-3-19
15 | // */
16 | //public final class HandCard implements Comparable {
17 | // private Card card;
18 | // private boolean isPicked; // 该卡牌是否被选中
19 | //
20 | // public HandCard(Card card) {
21 | // this.card = card;
22 | // isPicked = false;
23 | // }
24 | //
25 | // public Card getCard() {
26 | // return card;
27 | // }
28 | //
29 | // public boolean isPicked() {
30 | // return isPicked;
31 | // }
32 | //
33 | // public void setPicked(boolean isPicked) {
34 | // this.isPicked = isPicked;
35 | // }
36 | //
37 | // @Override
38 | // public int compareTo(HandCard another) {
39 | // return card.compareTo(another.card);
40 | // }
41 | //
42 | // @Override
43 | // public String toString() {
44 | // StringBuilder builder = new StringBuilder();
45 | // builder.append("HandCard [").append(card.getSuit()).append(" ")
46 | // .append(card.getValue()).append(isPicked ? ",picked" : "")
47 | // .append("]");
48 | // return builder.toString();
49 | // }
50 | //}
51 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/NonBombType.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | /**标记接口, 表示非炸弹类型。 */
4 | public interface NonBombType {
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/Pair.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * @author Muyangmin
7 | * @create 2015-3-23
8 | */
9 | public final class Pair extends CardType implements NonBombType {
10 |
11 | public Pair(ArrayList list) {
12 | if (list == null || list.size() != 2
13 | || (!(list.get(0).isSameValueAs(list.get(1))))) {
14 | throw new IllegalArgumentException("A pair must be 2 same cards!");
15 | }
16 | //保护性复制
17 | cardList = new ArrayList<>(list);
18 | }
19 |
20 | /**
21 | * 实现对子牌型大小比对。
22 | * @return 如果参数是炸弹,则始终返回负数;否则返回按点数的比较结果。
23 | * @see com.mym.landlords.card.CardType#compareTo(com.mym.landlords.card.CardType)
24 | */
25 | @Override
26 | public int compareTo(CardType another) {
27 | int superCompare = super.compareTo(another);
28 | if (superCompare != UNDEFINED_COMPARE) {
29 | return superCompare;
30 | }
31 | if (!(another instanceof Pair)) {
32 | throw new ClassCastException("compare to wrong object.");
33 | }
34 | return cardList.get(0).compareTo(((Pair) another).cardList.get(0));
35 | }
36 |
37 | @Override
38 | public String toString() {
39 | StringBuilder builder = new StringBuilder();
40 | builder.append("Pair ").append(cardList);
41 | return builder.toString();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/Rocket.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | import java.util.ArrayList;
4 |
5 | public final class Rocket extends CardType implements BombType{
6 | public Rocket(ArrayList list){
7 | if (list==null || list.size()!=2 || list.get(0).getSuit()!= CardSuit.Joker
8 | || list.get(1).getSuit()!= CardSuit.Joker){
9 | throw new IllegalArgumentException("this type must be 2 cards in joker suit!");
10 | }
11 | //保护性复制
12 | cardList = new ArrayList<>(list);
13 | }
14 |
15 | /**
16 | * 该方法将总是返回1。
17 | */
18 | @Override
19 | public int compareTo(CardType another){
20 | return 1;
21 | }
22 |
23 | @Override
24 | public String toString() {
25 | StringBuilder builder = new StringBuilder();
26 | builder.append("Rocket");
27 | return builder.toString();
28 | }
29 | }
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/Single.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * @author Muyangmin
7 | * @create 2015-3-23
8 | */
9 | public final class Single extends CardType implements NonBombType {
10 |
11 | public Single(Card card) {
12 | super();
13 | cardList = new ArrayList<>(1);
14 | cardList.add(card);
15 | }
16 | public Single(ArrayList cards) {
17 | super();
18 | if (cards==null || cards.size()>1){
19 | throw new IllegalArgumentException("a single type cannot contain more than 1 card.");
20 | }
21 | //保护性复制
22 | cardList = new ArrayList<>(cards);
23 | }
24 |
25 | /**
26 | * 实现单张牌牌型大小比对。
27 | * @return 如果参数是炸弹,则始终返回负数;否则返回按点数的比较结果。
28 | * @see com.mym.landlords.card.CardType#compareTo(com.mym.landlords.card.CardType)
29 | */
30 | @Override
31 | public int compareTo(CardType another){
32 | int superCompare = super.compareTo(another);
33 | if (superCompare!=UNDEFINED_COMPARE){
34 | return superCompare;
35 | }
36 | if (!(another instanceof Single)){
37 | throw new ClassCastException("compare to wrong object.");
38 | }
39 | return cardList.get(0).compareTo(
40 | ((Single) another).cardList.get(0));
41 | }
42 |
43 | @Override
44 | public String toString() {
45 | StringBuilder builder = new StringBuilder();
46 | builder.append("Single ").append(cardList);
47 | return builder.toString();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/Straight.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Iterator;
5 |
6 | /**
7 | * 顺子牌型。
8 | * @author Muyangmin
9 | * @create 2015-3-27
10 | */
11 | public class Straight extends CardType implements NonBombType{
12 |
13 | private int startValue;
14 | public final int length;
15 |
16 | public Straight(ArrayList list){
17 | if (list == null || (!canMakeupStraight(list))) {
18 | throw new IllegalArgumentException(
19 | "A straight must be made up of more than 5 continuous value."
20 | + list.toString());
21 | }
22 | //保护性复制
23 | this.cardList = new ArrayList<>(list);
24 | this.startValue = list.get(0).getValue();
25 | this.length = list.size();
26 | }
27 |
28 | private final boolean canMakeupStraight(ArrayList list){
29 | int listSize = list.size();
30 | if (listSize < 5){
31 | return false;
32 | }
33 | boolean res = true;
34 | Iterator iterator = list.iterator();
35 | //设定起始数值。由于list的长度在前面已经做过检验,因此这里的第一次 next操作一定是安全的。
36 | int lastValue = iterator.next().getValue();
37 | while (iterator.hasNext()){
38 | int currentValue = iterator.next().getValue();
39 | if ((currentValue != lastValue + 1) //检查每张牌的连续性
40 | || (currentValue >= Card.CARD_VALUE_2)) { //2和王不能被连
41 | res = false;
42 | break;
43 | }
44 | lastValue = currentValue;//更新迭代比较变量
45 | }
46 | return res;
47 | }
48 |
49 | @Override
50 | public int compareTo(CardType another){
51 | int superCompare = super.compareTo(another);
52 | if (superCompare != UNDEFINED_COMPARE){
53 | return superCompare;
54 | }
55 | return Integer.valueOf(startValue).compareTo(
56 | ((Straight) another).startValue);
57 | }
58 | //
59 | // @Override
60 | // protected boolean isSameConcreteSubclass(CardType another) {
61 | // if (another instanceof Straight){
62 | // return length == ((Straight)another).length;
63 | // }
64 | // return false;
65 | // }
66 | //
67 | @Override
68 | public String toString() {
69 | StringBuilder builder = new StringBuilder();
70 | builder.append("Straight [startValue=").append(startValue)
71 | .append(", length=").append(length).append(", cardList=")
72 | .append(cardList).append("]");
73 | return builder.toString();
74 | }
75 |
76 | }
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/card/Three.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.card;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 |
6 | /**
7 | * 因为斗地主中三条可以带一个或一对,因此需要稍加判断。但为了程序的统一性,三条和其带的牌被统一放在
8 | * 从父类继承的 {@link CardType#cardList}字段中,新增一个 {@link #getAttachType()}方法用于判定带的牌型,
9 | * 但并不应该做其他用途。
10 | * 例如,有一个 QAAA 对象(三个A带一个Q),则在出牌的时候 {@link #getCardList()}会返回四张牌,
11 | * 而 {@link #getAttachType()} 会返回一个Single对象,其值是 Q.
12 | * @author Muyangmin
13 | * @create 2015-3-24
14 | */
15 | public final class Three extends CardType implements NonBombType{
16 |
17 | private CardType attachType = null;
18 | private ArrayList bodyList;
19 |
20 | /**
21 | * 使用指定的卡牌列表进行带牌,程序将自动判断牌的组成。
22 | * @param list 要执行的卡牌列表;长度必须大于3.
23 | */
24 | public Three(ArrayList list) {
25 | try {
26 | divideListAndCreate(list);
27 | } catch (IllegalArgumentException e) {
28 | throw new IllegalArgumentException("wrong cards:" + list.toString());
29 | }
30 | }
31 |
32 | /**
33 | * 强制使用指定的三条进行带牌。
34 | * @param bodyList 三条的主体,列表长度必须为3。
35 | * @param attachment 带的牌,列表长度必须小于3。
36 | */
37 | public Three(ArrayList bodyList, ArrayList attachment){
38 | if (bodyList!=null && bodyList.size()==3){
39 | bodyList = new ArrayList<>(bodyList);
40 | cardList = new ArrayList<>(bodyList);
41 | if (attachment!=null){
42 | if (attachment.size()==1){
43 | attachType= new Single(attachment);
44 | }
45 | else{
46 | attachType = new Pair(attachment);
47 | }
48 | cardList.addAll(attachment);
49 | }
50 | return ;
51 | }
52 | throw new IllegalArgumentException("cannot makeup a three:body="
53 | + bodyList + ", attachment=" + attachment);
54 | }
55 |
56 | /**
57 | * 尝试识别三条和它所带的卡牌类型。
58 | * @param list
59 | * @throws IllegalArgumentException
60 | */
61 | private void divideListAndCreate(ArrayList list) throws IllegalArgumentException{
62 | if (list.size()<3 || list.size()>5){
63 | throw new IllegalArgumentException();
64 | }
65 | int cardValue=0, attachValue=0;
66 | ArrayList tempCardList = new ArrayList<>();
67 | ArrayList tempAttachList= new ArrayList<>();
68 | for (Card card: list){
69 | int currentValue = card.getValue();
70 | if (cardValue==0 || currentValue==cardValue){
71 | cardValue = currentValue;
72 | tempCardList.add(card);
73 | }
74 | else if (attachValue==0 || currentValue==attachValue) {
75 | attachValue=currentValue;
76 | tempAttachList.add(card);
77 | }
78 | //出现了第三种值
79 | else {
80 | throw new IllegalArgumentException();
81 | }
82 | }
83 | //检查元素的个数
84 | int tmpListSize = tempCardList.size();
85 | int tmpAttachSize = tempAttachList.size();
86 | if ( (tmpListSize==3 && (tmpAttachSize<3)) ){
87 | bodyList = tempCardList;
88 | }
89 | else if (tmpAttachSize==3 && (tmpListSize<3)){
90 | bodyList = tempAttachList;
91 | tempAttachList = tempCardList;
92 | tmpAttachSize = tmpListSize;
93 | }
94 | else{
95 | throw new IllegalArgumentException();
96 | }
97 | //保护性复制
98 | cardList = new ArrayList<>(list);
99 | if (tmpListSize == 1) {
100 | this.attachType = new Single(tempCardList);
101 | } else if (tmpAttachSize == 2) {
102 | this.attachType = new Pair(tempCardList);
103 | }
104 | }
105 |
106 | /**
107 | * 实现三张牌型大小比对。
108 | * @return 如果参数是炸弹,则始终返回负数;否则返回按点数的比较结果。
109 | * @see com.mym.landlords.card.CardType#compareTo(com.mym.landlords.card.CardType)
110 | */
111 | @Override
112 | public int compareTo(CardType another){
113 | int superCompare = super.compareTo(another);
114 | if (superCompare != UNDEFINED_COMPARE){
115 | return superCompare;
116 | }
117 | if (!(another instanceof Three)) {
118 | throw new ClassCastException("compare to wrong type:"
119 | + another.getClass().getSimpleName());
120 | }
121 | return bodyList.get(0).compareTo(((Three)another).bodyList.get(0));
122 | }
123 |
124 | @Override
125 | public String toString() {
126 | StringBuilder builder = new StringBuilder();
127 | builder.append("Three ").append(cardList);
128 | return builder.toString();
129 | }
130 |
131 | /**
132 | * 获得所带的牌型,可能为null,Single或 Pair三种类型。
133 | */
134 | public CardType getAttachType() {
135 | return attachType;
136 | }
137 | /**
138 | * 获得组成三条的卡牌列表。
139 | */
140 | public ArrayList getBodyList(){
141 | return bodyList;
142 | }
143 |
144 | //设置这个三条所带的牌。
145 | public void setAttachType(CardType attachType) {
146 | CardType beforeType = this.attachType;
147 | if (attachType!=null && beforeType!=null){
148 | cardList.removeAll(beforeType.getCardList());
149 | }
150 | this.attachType = attachType;
151 | if (attachType!=null){
152 | cardList.addAll(attachType.getCardList());
153 | Collections.sort(cardList, Card.COMPARATOR_WITH_SUIT);
154 | }
155 | }
156 |
157 | }
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/res/Assets.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.res;
2 |
3 | import com.mym.landlords.card.Card;
4 | import com.mym.landlords.card.CardSuit;
5 | import com.mym.landlords.widget.MappedTouchEvent;
6 |
7 | import android.app.Activity;
8 | import android.content.Context;
9 | import android.graphics.Point;
10 | import android.util.Log;
11 | import android.view.WindowManager;
12 |
13 | /**
14 | * 负责加载并存储从Assets文件中加载到内存中的资源。
15 | *
16 | *
加载数据
17 | * 使用 {@link #loadAssets(Context, LoadingProgressListener)}方法进行资源文件的加载,加载进度会通过参数中的接口进行传递。
18 | * 注意:在调用 {@link #getInstance()}获得该类的实例之前必须确保 load方法已被调用过,否则将抛出异常。
19 | *
20 | *
21 | *
数据安全
22 | * 因加载的资源较多,且多使用数组或列表的形式存放,因此对这些数据的安全性不做任何强制性保证,
23 | * 但在代码编写过程中需要注意避免为该类的任何域赋值。
24 | *
25 | * @author Muyangmin
26 | * @create 2015-3-16
27 | */
28 | public final class Assets {
29 |
30 | private static final String LOG_TAG = "Assets";
31 |
32 | private static Assets instance;
33 |
34 | /** 牌桌背景。 */
35 | public LiveBitmap bkgGameTable;
36 |
37 | /** 黑桃卡牌,按卡牌从小到大排序。 */
38 | public LiveBitmap[] cardSpades = new LiveBitmap[13];;
39 | /** 红桃卡牌,按卡牌从小到大排序。 */
40 | public LiveBitmap[] cardHearts = new LiveBitmap[13];;
41 | /** 梅花卡牌,按卡牌从小到大排序。 */
42 | public LiveBitmap[] cardClubs = new LiveBitmap[13];;
43 | /** 方片卡牌,按卡牌从小到大排序。 */
44 | public LiveBitmap[] cardDiamonds = new LiveBitmap[13];;
45 |
46 | /** 小型黑桃卡牌(仅有左上角的数字和花色,主要用于展示底牌),按卡牌从小到大排序。 */
47 | public LiveBitmap[] cardSmallSpades = new LiveBitmap[13];;
48 | /** 小型红桃卡牌(仅有左上角的数字和花色,主要用于展示底牌),按卡牌从小到大排序。 */
49 | public LiveBitmap[] cardSmallHearts = new LiveBitmap[13];;
50 | /** 小型梅花卡牌(仅有左上角的数字和花色,主要用于展示底牌),按卡牌从小到大排序。 */
51 | public LiveBitmap[] cardSmallClubs = new LiveBitmap[13];;
52 | /** 小型方片卡牌(仅有左上角的数字和花色,主要用于展示底牌),按卡牌从小到大排序。 */
53 | public LiveBitmap[] cardSmallDiamonds = new LiveBitmap[13];;
54 |
55 | /** 小王卡牌。注意:王不分大小图。 */
56 | public LiveBitmap cardJokerS;
57 | /** 大王卡牌。 注意:王不分大小图。*/
58 | public LiveBitmap cardJokerB;
59 |
60 | /** 卡牌背面图。 */
61 | public LiveBitmap cardbg;
62 | /** 数字信息图片。 */
63 | public LiveBitmap bitmapNumbers;
64 | /** AI形象左。 */
65 | public LiveBitmap playerLeft;
66 | /** 玩家形象。 */
67 | public LiveBitmap playerHuman;
68 | /** AI形象右。 */
69 | public LiveBitmap playerRight;
70 | /** 按钮背景图。 */
71 | public LiveBitmap bitmapBtnBkg;
72 | /** 按钮背景图。 */
73 | public LiveBitmap bitmapBtnBkgPressed;
74 | /** 叫地主按钮:不叫。 */
75 | public LiveBitmap bitmapLandlordPass;
76 | /** 叫地主按钮:1分。 */
77 | public LiveBitmap bitmapLandlordP1;
78 | /** 叫地主按钮:2分。 */
79 | public LiveBitmap bitmapLandlordP2;
80 | /** 叫地主按钮:3分。 */
81 | public LiveBitmap bitmapLandlordP3;
82 | /** 出牌按钮。 */
83 | public LiveBitmap bitmapGiveCard;
84 | /** 不出按钮。 */
85 | public LiveBitmap bitmapDoNotGiveCard;
86 | /** 重选按钮。 */
87 | public LiveBitmap bitmapRechoose;
88 | /** 没有牌能大过上家。 */
89 | public LiveBitmap bitmapNoBigger;
90 | /** 选择的牌不符合规则 */
91 | public LiveBitmap bitmapCardsNotMatch;
92 | /** 提示按钮。 */
93 | public LiveBitmap bitmapTips;
94 | /** 地主图标 */
95 | public LiveBitmap iconLandlord;
96 |
97 | /** 叫地主音效:不叫。 */
98 | public int soundLandloadPass;
99 | /** 叫地主音效:1分。 */
100 | public int soundLandloadP1;
101 | /** 叫地主音效:2分。 */
102 | public int soundLandloadP2;
103 | /** 叫地主音效:3分。 */
104 | public int soundLandloadP3;
105 |
106 | /** 单牌音效:小王。 */
107 | public int soundCardJokerS;
108 | /** 单牌音效:大王。 */
109 | public int soundCardJokerB;
110 |
111 | /** 牌型音效:单。 */
112 | public int soundTypeSingle;
113 | /** 牌型音效:对。 */
114 | public int soundTypePair;
115 | /** 牌型音效:三不带。 */
116 | public int soundTypeThree;
117 | /** 牌型音效:三带一。 */
118 | public int soundTypeThree1;
119 | /** 牌型音效:三带对。 */
120 | public int soundTypeThree2;
121 | /** 牌型音效:飞机。 */
122 | public int soundTypePlane;
123 | /** 牌型音效:顺子。 */
124 | public int soundTypeShun1;
125 | /** 牌型音效:双顺(连对)。 */
126 | public int soundTypeShun2;
127 | /** 牌型音效:炸弹。 */
128 | public int soundTypeBomb;
129 | /** 牌型音效: 王炸(有些地方称为火箭)。 */
130 | public int soundTypeRocket;
131 |
132 | /** 牌局音效:过。 */
133 | public int soundPlayPass;
134 | /** 牌局音效:跟牌压牌。 */
135 | public int[] soundPlayBigger;
136 | /** 牌局音效:炸弹爆炸。 */
137 | public int soundEffectBoom;
138 | /** 牌局音效:飞机。 */
139 | public int soundEffectPlane;
140 | /** 牌局音效:胜。 */
141 | public int soundPlayWin;
142 | /** 牌局音效:负。 */
143 | public int soundPlayLose;
144 |
145 | /**
146 | * 获取该类的唯一实例。
147 | * @throws RuntimeException 如果之前未调用过 {@link #loadAssets(Context, LoadingProgressListener)}方法。
148 | */
149 | public static final Assets getInstance(){
150 | if (instance==null){
151 | throw new RuntimeException("you must call load() method before getInstance()!");
152 | }
153 | return instance;
154 | }
155 |
156 | /**
157 | * 加载Assets资源,该方法应该仅被调用一次。
158 | * @param context 上下文信息,不能为null。
159 | * @param listener 进度监听器,不能为null。
160 | * @throws RuntimeException 如果之前已经调用过该方法则抛出此异常。
161 | */
162 | public static synchronized void loadAssets(Context context, LoadingProgressListener listener){
163 | if (instance!=null){
164 | // throw new RuntimeException("Assets resources have been already loaded.");
165 | //这里不假定instance不为Null即代表其所持有的内存资源没有被释放,为了安全起见,强制重新加载。
166 | Log.e(LOG_TAG, "Assets resources have been already loaded.Force reloading...");
167 | //强制释放旧的资源,否则如果短时间多次频繁开关应用,会导致OOM。
168 | instance.recycleOldBitmap();
169 | Log.d(LOG_TAG, "old resources cleared.");
170 | instance = null;
171 | }
172 | if (context==null || listener==null){
173 | throw new NullPointerException("params cannot be null.");
174 | }
175 | instance = new Assets();
176 | instance.load(context, listener);
177 | }
178 |
179 | private final synchronized void recycleOldBitmap(){
180 | recycleBitmap(bitmapNumbers);
181 | recycleBitmap(bkgGameTable);
182 | recycleBitmap(cardSpades);
183 | recycleBitmap(cardHearts);
184 | recycleBitmap(cardClubs);
185 | recycleBitmap(cardDiamonds);
186 | recycleBitmap(cardSmallSpades);
187 | recycleBitmap(cardSmallHearts);
188 | recycleBitmap(cardSmallClubs);
189 | recycleBitmap(cardDiamonds);
190 | recycleBitmap(cardJokerB);
191 | recycleBitmap(cardJokerS);
192 | recycleBitmap(iconLandlord);
193 | recycleBitmap(playerHuman);
194 | recycleBitmap(playerLeft);
195 | recycleBitmap(playerRight);
196 | recycleBitmap(bitmapLandlordPass);
197 | recycleBitmap(bitmapLandlordP1);
198 | recycleBitmap(bitmapLandlordP2);
199 | recycleBitmap(bitmapLandlordP3);
200 | }
201 |
202 | private final void recycleBitmap(LiveBitmap[] array){
203 | if (array!=null){
204 | for (LiveBitmap bitmap : array){
205 | recycleBitmap(bitmap);
206 | }
207 | }
208 | }
209 |
210 | private final void recycleBitmap(LiveBitmap bitmap){
211 | try {
212 | if (bitmap!=null){
213 | bitmap.getBitmap().recycle();
214 | }
215 | } catch (Exception e) {
216 | Log.w(LOG_TAG, "exception while recycling bitmap");
217 | e.printStackTrace();
218 | }
219 | }
220 |
221 | /**
222 | * 执行实际的加载操作,由于公有方法中做了参数检查,这里不再检查。
223 | */
224 | private final void load(Context context, LoadingProgressListener listener){
225 | final int totalBitmap = 54 + 52 //54=一副牌,52=一副牌的较小版本
226 | + 1 + 1 + 2 //卡牌背面图、牌桌背景图、按钮背景图
227 | + 1 //数字图
228 | + 4 + 6 //叫地主按钮、出牌系列按钮
229 | + 3 + 1; //人物形象图、地主图标
230 | final int totalSoundCount = 24; //数出来的
231 | int total = totalBitmap + totalSoundCount;
232 | int loadedBitmap = loadBitmap(0, total, context, listener);
233 | int loadedRes = loadSound(loadedBitmap, total, context, listener);
234 | //安全检查,避免有遗漏忘记加载的资源。
235 | if (loadedRes!=total){
236 | throw new RuntimeException("Resource loaded "+loadedRes+", but it was declared to be "+total);
237 | }
238 | listener.onLoadCompleted();
239 | }
240 |
241 | /**
242 | * 加载声音资源
243 | * @param hasCompleted 已加载的资源数目
244 | * @param total 全部资源总数
245 | * @return 返回已加载的资源数目。
246 | */
247 | private final int loadSound(final int hasCompleted, final int total,
248 | Context context, LoadingProgressListener listener) {
249 | GlobalSoundPool sp = GlobalSoundPool.getInstance(context);
250 | int completed = hasCompleted; //复位
251 | soundLandloadP1 = sp.loadSound(context, "landlord_p1.mp3");
252 | notifyProgressChanged(++completed, total, listener);
253 | soundLandloadP2 = sp.loadSound(context, "landlord_p2.mp3");
254 | notifyProgressChanged(++completed, total, listener);
255 | soundLandloadP3 = sp.loadSound(context, "landlord_p3.mp3");
256 | notifyProgressChanged(++completed, total, listener);
257 | soundLandloadPass = sp.loadSound(context, "landlord_pass.mp3");
258 | notifyProgressChanged(++completed, total, listener);
259 | soundTypePlane = sp.loadSound(context, "feiji.wav");
260 | notifyProgressChanged(++completed, total, listener);
261 | soundTypeBomb = sp.loadSound(context, "zhadan.wav");
262 | notifyProgressChanged(++completed, total, listener);
263 | soundCardJokerS = sp.loadSound(context, "xiaowang.wav");
264 | notifyProgressChanged(++completed, total, listener);
265 | soundCardJokerB = sp.loadSound(context, "dawang.wav");
266 | notifyProgressChanged(++completed, total, listener);
267 | soundTypeRocket = sp.loadSound(context, "wangzha.wav");
268 | notifyProgressChanged(++completed, total, listener);
269 | soundTypeShun1 = sp.loadSound(context, "shunzi.wav");
270 | notifyProgressChanged(++completed, total, listener);
271 | soundTypeShun2 = sp.loadSound(context, "liandui.wav");
272 | notifyProgressChanged(++completed, total, listener);
273 | soundTypeSingle = sp.loadSound(context, "givecard.wav");
274 | notifyProgressChanged(++completed, total, listener);
275 | soundTypeThree = sp.loadSound(context, "givecard.wav");
276 | notifyProgressChanged(++completed, total, listener);
277 | soundTypeThree1 = sp.loadSound(context, "sandaiyi.wav");
278 | notifyProgressChanged(++completed, total, listener);
279 | soundTypeThree2 = sp.loadSound(context, "sandaiyidui.wav");
280 | notifyProgressChanged(++completed, total, listener);
281 | soundTypePair = sp.loadSound(context, "givecard.wav");
282 | notifyProgressChanged(++completed, total, listener);
283 |
284 | soundPlayBigger = new int[3];
285 | soundPlayBigger[0] = sp.loadSound(context, "dani1.wav");
286 | notifyProgressChanged(++completed, total, listener);
287 | soundPlayBigger[1] = sp.loadSound(context, "dani2.wav");
288 | notifyProgressChanged(++completed, total, listener);
289 | soundPlayBigger[2] = sp.loadSound(context, "dani3.wav");
290 | notifyProgressChanged(++completed, total, listener);
291 | soundPlayPass = sp.loadSound(context, "buyao.wav");
292 | notifyProgressChanged(++completed, total, listener);
293 | soundEffectBoom = sp.loadSound(context, "boom.wav");
294 | notifyProgressChanged(++completed, total, listener);
295 | soundEffectPlane = sp.loadSound(context, "plane.wav");
296 | notifyProgressChanged(++completed, total, listener);
297 | soundPlayLose = sp.loadSound(context, "lose.mp3");
298 | notifyProgressChanged(++completed, total, listener);
299 | soundPlayWin = sp.loadSound(context, "win.mp3");
300 | notifyProgressChanged(++completed, total, listener);
301 | return completed;
302 | }
303 |
304 | private final int loadBitmap(final int hasCompleted, final int total,
305 | Context context,
306 | LoadingProgressListener listener) {
307 | if (!(context instanceof Activity)) {
308 | throw new IllegalArgumentException(
309 | "This context must be instance of activity.");
310 | }
311 | WindowManager wm = ((Activity) context).getWindowManager();
312 | Point point = new Point();
313 | wm.getDefaultDisplay().getSize(point);
314 | Log.d(LOG_TAG, "window point:" + point.toString());
315 | //重要:这里将画笔工具和事件捕捉类的缩放比例一起设置。
316 | GameGraphics.initGraphicsScale(point);
317 | GameGraphics graphics = GameGraphics.newInstance();
318 | final float scaleX = graphics.getScaleX();
319 | final float scaleY = graphics.getScaleY();
320 | Log.d(LOG_TAG, "scalex = "+scaleX+", scaleY="+scaleY);
321 | MappedTouchEvent.initMapper(scaleX, scaleY);
322 |
323 | int completed = hasCompleted;
324 | // 加载黑桃图片
325 | for (int i = 0, value = Card.CARD_VALUE_3; i < 13; i++, value++) {
326 | cardSpades[i] = LiveBitmap.loadBitmap(context,
327 | getCardAssetsName(CardSuit.Spade, value, false), scaleX,
328 | scaleY);
329 | cardSmallSpades[i] = LiveBitmap.loadBitmap(context,
330 | getCardAssetsName(CardSuit.Spade, value, true), scaleX,
331 | scaleY);
332 | completed += 2;
333 | notifyProgressChanged(completed, total, listener);
334 | }
335 | // 加载红桃图片
336 | for (int i = 0, value = Card.CARD_VALUE_3; i < 13; i++, value++) {
337 | cardHearts[i] = LiveBitmap.loadBitmap(context,
338 | getCardAssetsName(CardSuit.Heart, value, false), scaleX,
339 | scaleY);
340 | cardSmallHearts[i] = LiveBitmap.loadBitmap(context,
341 | getCardAssetsName(CardSuit.Heart, value, true), scaleX,
342 | scaleY);
343 | completed += 2;
344 | notifyProgressChanged(completed, total, listener);
345 | }
346 | // 加载梅花图片
347 | for (int i = 0, value = Card.CARD_VALUE_3; i < 13; i++, value++) {
348 | cardClubs[i] = LiveBitmap.loadBitmap(context,
349 | getCardAssetsName(CardSuit.Club, value, false), scaleX,
350 | scaleY);
351 | cardSmallClubs[i] = LiveBitmap.loadBitmap(context,
352 | getCardAssetsName(CardSuit.Club, value, true), scaleX,
353 | scaleY);
354 | completed += 2;
355 | notifyProgressChanged(completed, total, listener);
356 | }
357 | // 加载方片图片
358 | for (int i = 0, value = Card.CARD_VALUE_3; i < 13; i++, value++) {
359 | cardDiamonds[i] = LiveBitmap.loadBitmap(context,
360 | getCardAssetsName(CardSuit.Diamond, value, false), scaleX,
361 | scaleY);
362 | cardSmallDiamonds[i] = LiveBitmap.loadBitmap(context,
363 | getCardAssetsName(CardSuit.Diamond, value, true), scaleX,
364 | scaleY);
365 | completed += 2;
366 | notifyProgressChanged(completed, total, listener);
367 | }
368 | cardJokerS = LiveBitmap.loadBitmap(
369 | context,
370 | getCardAssetsName(CardSuit.Joker, Card.CARD_VALUE_JOKER_S,
371 | false), scaleX, scaleY);
372 | cardJokerB = LiveBitmap.loadBitmap(
373 | context,
374 | getCardAssetsName(CardSuit.Joker, Card.CARD_VALUE_JOKER_B,
375 | false), scaleX, scaleY);
376 |
377 | completed += 2;
378 | notifyProgressChanged(completed, total, listener);
379 | cardbg = LiveBitmap.loadBitmap(context, "cardbg.png", scaleX, scaleY);
380 | notifyProgressChanged(++completed, total, listener);
381 | bkgGameTable = LiveBitmap.loadBitmap(context, "bkg_table.png", scaleX, scaleY);
382 | notifyProgressChanged(++completed, total, listener);
383 | bitmapNumbers = LiveBitmap.loadBitmap(context, "numbers.png", scaleX, scaleY);
384 | notifyProgressChanged(++completed, total, listener);
385 |
386 | playerLeft = LiveBitmap.loadBitmap(context, "playerLeft.png", scaleX, scaleY);
387 | playerHuman = LiveBitmap.loadBitmap(context, "playerHuman.png", scaleX, scaleY);
388 | playerRight = LiveBitmap.loadBitmap(context, "playerRight.png", scaleX, scaleY);
389 | completed +=3 ;
390 | notifyProgressChanged(completed, total, listener);
391 | iconLandlord = LiveBitmap.loadBitmap(context, "icLandlord.png", scaleX, scaleY);
392 | notifyProgressChanged(++completed, total, listener);
393 |
394 | //加载按钮图
395 | bitmapBtnBkg = LiveBitmap.loadBitmap(context, "bkg_btn_normal.png", scaleX, scaleY);
396 | notifyProgressChanged(++completed, total, listener);
397 | bitmapBtnBkgPressed = LiveBitmap.loadBitmap(context, "bkg_btn_pressed.png", scaleX, scaleY);
398 | notifyProgressChanged(++completed, total, listener);
399 | bitmapLandlordPass = LiveBitmap.loadBitmap(context, "landlord_pass.png", scaleX, scaleY);
400 | bitmapLandlordP1 = LiveBitmap.loadBitmap(context, "landlord_p1.png", scaleX, scaleY);
401 | bitmapLandlordP2 = LiveBitmap.loadBitmap(context, "landlord_p2.png", scaleX, scaleY);
402 | bitmapLandlordP3 = LiveBitmap.loadBitmap(context, "landlord_p3.png", scaleX, scaleY);
403 | completed +=4;
404 | notifyProgressChanged(completed, total, listener);
405 |
406 | bitmapGiveCard = LiveBitmap.loadBitmap(context, "play_ahand.png", scaleX, scaleY);
407 | bitmapDoNotGiveCard = LiveBitmap.loadBitmap(context, "play_pass.png", scaleX, scaleY);
408 | bitmapRechoose = LiveBitmap.loadBitmap(context, "play_rechoose.png", scaleX, scaleY);
409 | bitmapTips = LiveBitmap.loadBitmap(context, "play_tip.png", scaleX, scaleY);
410 | completed +=4;
411 | notifyProgressChanged(completed, total, listener);
412 |
413 | bitmapNoBigger = LiveBitmap.loadBitmap(context, "noBiggerCards.png", scaleX, scaleY);
414 | bitmapCardsNotMatch = LiveBitmap.loadBitmap(context, "wrongCards.png", scaleX, scaleY);
415 | completed +=2;
416 | notifyProgressChanged(completed, total, listener);
417 | return completed;
418 | }
419 |
420 | //通知加载的进度
421 | private final void notifyProgressChanged(int completed, int total,
422 | LoadingProgressListener listener) {
423 | int progress = (int) (completed/(double)total*100);
424 | listener.onProgressChanged(progress);
425 | }
426 |
427 | /**
428 | * 拼接卡牌文件名。
429 | * @param type 卡牌花色。
430 | * @param value 卡牌面值。
431 | * @param isSmaller 是整张还是仅左上角。
432 | * @return 返回拼接好的文件名。
433 | */
434 | private String getCardAssetsName(CardSuit type, int value, boolean isSmaller){
435 | StringBuilder sb = new StringBuilder();
436 | char typePrefix;
437 | switch (type) {
438 | case Spade : typePrefix = 's'; break;
439 | case Heart : typePrefix = 'h'; break;
440 | case Club : typePrefix = 'c'; break;
441 | case Diamond: typePrefix = 'd'; break;
442 | case Joker :
443 | if (value == Card.CARD_VALUE_JOKER_S){
444 | return "j1.png";
445 | }
446 | else if(value==Card.CARD_VALUE_JOKER_B){
447 | return "j2.png";
448 | }
449 | else{
450 | throw new RuntimeException("invalid value : "+value);
451 | }
452 | default: throw new RuntimeException("invalid type : "+type);
453 | }
454 | sb.append(typePrefix).append(value);
455 | if (isSmaller){
456 | sb.append('s');
457 | }
458 | sb.append(".png");
459 | return sb.toString();
460 | }
461 |
462 | /**
463 | * 获得卡牌对应的位图引用。
464 | * @param card 卡牌对象
465 | * @return 返回卡牌对应的位图。
466 | */
467 | public final LiveBitmap getCorrespondBitmap(Card card){
468 | if (card==null){
469 | return null;
470 | }
471 | switch (card.getSuit()) {
472 | case Spade:
473 | return cardSpades[card.getValue() - Card.CARD_VALUE_3];
474 | case Heart:
475 | return cardHearts[card.getValue() - Card.CARD_VALUE_3];
476 | case Club:
477 | return cardClubs[card.getValue() - Card.CARD_VALUE_3];
478 | case Diamond:
479 | return cardDiamonds[card.getValue() - Card.CARD_VALUE_3];
480 | case Joker:
481 | if (card.getValue() == Card.CARD_VALUE_JOKER_B) {
482 | return cardJokerB;
483 | } else {
484 | return cardJokerS;
485 | }
486 | default:
487 | return null;
488 | }
489 | }
490 |
491 | /**
492 | * 获得卡牌对应的小图引用。
493 | * @param card 卡牌对象
494 | * @return 返回卡牌对应的位图。
495 | */
496 | public final LiveBitmap getCorrespondSmallBitmap(Card card){
497 | if (card==null){
498 | return null;
499 | }
500 | switch (card.getSuit()) {
501 | case Spade:
502 | return cardSmallSpades[card.getValue() - Card.CARD_VALUE_3];
503 | case Heart:
504 | return cardSmallHearts[card.getValue() - Card.CARD_VALUE_3];
505 | case Club:
506 | return cardSmallClubs[card.getValue() - Card.CARD_VALUE_3];
507 | case Diamond:
508 | return cardSmallDiamonds[card.getValue() - Card.CARD_VALUE_3];
509 | case Joker:
510 | if (card.getValue() == Card.CARD_VALUE_JOKER_B) {
511 | return cardJokerB;
512 | } else {
513 | return cardJokerS;
514 | }
515 | default:
516 | return null;
517 | }
518 | }
519 |
520 | /**
521 | * 监听Assets资源的加载进度。
522 | * @author Muyangmin
523 | * @create 2015-3-16
524 | */
525 | public interface LoadingProgressListener{
526 | /**
527 | * 加载进度改变时调用。
528 | * 注意Task的概念,即将加载分为多个步骤(例如卡牌、音效...),每步中有0~100的进度。
529 | * @param progress 当前进度,取值在0~100之间。
530 | * @param currentTaskDescription 当前任务描述。
531 | */
532 | void onProgressChanged(int progress/*, String currentTaskDescription*/);
533 | /**
534 | * 当所有加载资源完成时调用。
535 | */
536 | void onLoadCompleted();
537 | }
538 |
539 | }
540 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/res/GameGraphics.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.res;
2 |
3 | import com.mym.landlords.ai.Player;
4 |
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.Point;
8 | import android.graphics.Rect;
9 | import android.graphics.Typeface;
10 | import android.os.CountDownTimer;
11 |
12 | /**
13 | * 负责图像的绘制和自动缩放控制。
14 | * @author Muyangmin
15 | * @create 2015-3-17
16 | */
17 | public final class GameGraphics {
18 |
19 | /** 基准屏幕宽度。 */
20 | public static final int BASE_SCREEN_WIDTH = 800;
21 | /** 基准屏幕高度。 */
22 | public static final int BASE_SCREEN_HEIGHT = 480;
23 | /** 卡牌原始宽度。 */
24 | public static final int CARD_WIDTH = 92;
25 | /** 卡牌原始高度。 */
26 | public static final int CARD_HEIGHT = 126;
27 | /** 卡牌被选中后向上抽出的高度。 */
28 | public static final int Card_PICKED_OFFSET = 10;
29 | /** 游戏屏幕的水平内边距[离屏幕左右边界]。 */
30 | public static final int SCREEN_PADDING_HORIZONTAL = 3;
31 | /** 游戏屏幕的垂直内边距[离屏幕上下边界]。 */
32 | public static final int SCREEN_MARGIN_VERTICAL = 3;
33 | /** AI 玩家所剩手牌数目文字X坐标。 */
34 | public static final int AIPLAYER_LEFT_CARDNUM_X =25; // org=30
35 | /** AI 玩家所剩手牌数目文字X坐标。 */
36 | public static final int AIPLAYER_RIGHT_CARDNUM_X=730;//org=750
37 | /** AI 玩家所剩手牌数目文字Y坐标。 */
38 | public static final int AIPLAYER_CARDNUM_MARGIN_Y=100;
39 |
40 |
41 | // private Bitmap frameBuffer; // 底色
42 | // private Canvas canvas; // 画布对象
43 | private float scaleX; // X缩放比
44 | private float scaleY; // Y缩放比
45 | private Rect srcRect = new Rect(); // 源矩阵对象
46 | private Rect dstRect = new Rect(); // 目标矩阵对象
47 |
48 | //下面三个用于绘制渐消的信息
49 | private AutoDecendAlphaPaint humanAlphaPaint;
50 | private AutoDecendAlphaPaint leftAlphaPaint;
51 | private AutoDecendAlphaPaint rightAlphaPaint;
52 |
53 | private static GameGraphics instance;
54 |
55 | //初始化屏幕缩放比例。该方法仅被Assets调用。
56 | protected static synchronized void initGraphicsScale(Point outSize){
57 | instance = new GameGraphics(outSize);
58 | }
59 |
60 | /**
61 | * 获得画笔对象。
62 | */
63 | public static GameGraphics newInstance(){
64 | if (instance==null){
65 | throw new RuntimeException("must call initGraphicsScale() before create instance.");
66 | }
67 | return instance;
68 | }
69 | private GameGraphics(Point outSize){
70 | scaleX = outSize.x / (float) BASE_SCREEN_WIDTH;
71 | scaleY = outSize.y / (float) BASE_SCREEN_HEIGHT;
72 | this.humanAlphaPaint = new AutoDecendAlphaPaint();
73 | this.leftAlphaPaint = new AutoDecendAlphaPaint();
74 | this.rightAlphaPaint = new AutoDecendAlphaPaint();
75 | initTextPaintEffect(leftAlphaPaint);
76 | initTextPaintEffect(rightAlphaPaint);
77 | }
78 |
79 | private void initTextPaintEffect(Paint paint){
80 | paint.setAntiAlias(true);
81 | paint.setARGB(255, 255, 255, 255);
82 | paint.setTextSize(20*scaleX);
83 | paint.setStrokeWidth(5);
84 | paint.setTypeface(Typeface.DEFAULT_BOLD);
85 | }
86 |
87 | /**
88 | * 绘制指定的Bitmap。
89 | * @param canvas 画布
90 | * @param bitmap 要绘制的图片。
91 | * @param x 目标左边缘位置
92 | * @param y 目标上边缘位置
93 | * @param srcX 缩放前左边缘位置
94 | * @param srcY 缩放前上边缘位置
95 | * @param srcWidth 缩放前宽度
96 | * @param srcHeight 缩放前高度
97 | */
98 | public void drawBitmap(Canvas canvas, LiveBitmap bitmap, int x, int y, int srcX, int srcY,
99 | int srcWidth, int srcHeight) {
100 | srcRect.left = (int) (srcX * scaleX + 0.5f);
101 | srcRect.top = (int) (srcY * scaleY + 0.5f);
102 | srcRect.right = (int) ((srcX + srcWidth - 1) * scaleX + 0.5f);
103 | srcRect.bottom = (int) ((srcY + srcHeight - 1) * scaleY + 0.5f);
104 |
105 | dstRect.left = (int) (x * scaleX + 0.5f);
106 | dstRect.top = (int) (y * scaleY + 0.5);
107 | dstRect.right = (int) ((x + srcWidth - 1) * scaleX + 0.5f);
108 | dstRect.bottom = (int) ((y + srcHeight - 1) * scaleY + 0.5f);
109 |
110 | canvas.drawBitmap(bitmap.getBitmap(), srcRect, dstRect, null);
111 | }
112 |
113 | /**
114 | * 设置画笔的Alpha值。该值仅对调用 {@link #drawBitmapUsingAlpha(Canvas, LiveBitmap, int, int)}有效。
115 | * 注意:强烈建议在主线程中调用该方法。
116 | * @param alpha 目标 alpha值,必须在0-255之间。
117 | */
118 | public final void setAlpha(Player player, int alpha){
119 | getCorrespondingPaint(player).setCurrentAlpha(alpha);
120 | }
121 |
122 | /**
123 | * 获得当前的Alpha值。注意:每个玩家的信息Alpha值通常是不相等的。
124 | * @param player 玩家信息
125 | * @return 返回当前Alpha值。
126 | */
127 | public final int getCurrentAlpha(Player player){
128 | return getCorrespondingPaint(player).getCurrentAlpha();
129 | }
130 |
131 | private final AutoDecendAlphaPaint getCorrespondingPaint(Player player){
132 | if (!player.isAiPlayer()){
133 | return humanAlphaPaint;
134 | }
135 | //右手AI
136 | else if (player.getNextPlayer().isAiPlayer()){
137 | return rightAlphaPaint;
138 | }
139 | //左手AI
140 | else{
141 | return leftAlphaPaint;
142 | }
143 | }
144 |
145 | /**
146 | * 使用 {@link #currentAlpha}作为Alpha值绘制Bitmap。
147 | * 可以通过调用 {@link #setAlpha(int)}来设置Alpha值。Alpha值会在设置之后自动递减,直至为0。
148 | */
149 | public void drawBitmapUsingAlpha(Canvas canvas, Player player,
150 | LiveBitmap bitmap, int x, int y) {
151 | canvas.drawBitmap(bitmap.getBitmap(), x * scaleX, y * scaleY,
152 | getCorrespondingPaint(player));
153 | }
154 |
155 | /**
156 | * 绘制指定的Bitmap,自动处理缩放比例。
157 | * @param canvas 目标画布
158 | * @param bitmap 要绘制的图片
159 | * @param x 左边缘位置
160 | * @param y 上边缘位置
161 | */
162 | public void drawBitmap(Canvas canvas, LiveBitmap bitmap, int x, int y) {
163 | canvas.drawBitmap(bitmap.getBitmap(), x * scaleX, y * scaleY, null);//不需要Paint对象
164 | }
165 |
166 | /**
167 | * 绘制指定的Bitmap
168 | * @param bitmap 要绘制的Bitmap。
169 | * @param x 目标左边缘位置
170 | * @param y 目标上边缘位置
171 | * @param srcWidth 缩放前宽度
172 | * @param srcHeight 缩放前高度
173 | */
174 | public void drawBitmap(Canvas canvas, LiveBitmap bitmap, int x, int y, int srcWidth,
175 | int srcHeight) {
176 | dstRect.left = (int) (x * scaleX + 0.5f);
177 | dstRect.top = (int) (y * scaleY + 0.5);
178 | dstRect.right = (int) ((x + srcWidth - 1) * scaleX + 0.5f);
179 | dstRect.bottom = (int) ((y + srcHeight - 1) * scaleY + 0.5f);
180 | canvas.drawBitmap(bitmap.getBitmap(), null, dstRect, null);
181 | }
182 |
183 | public void drawBitmapInParentCenter(Canvas canvas, LiveBitmap bitmap, Point center) {
184 | int x = center.x - (int) (bitmap.getRawWidth() / 2 + 0.5f);
185 | int y = center.y - (int) (bitmap.getRawHeight() / 2 + 0.5f);
186 | drawBitmap(canvas, bitmap, x, y);
187 | }
188 | /**
189 | * 绘制数字形式的文本。
190 | * @param msg 要绘制的消息,只能包含数字或空格,否则将抛出异常。
191 | * @param numbeBitmap 要绘制的数字图片。
192 | * @param x 文字的起点坐标
193 | * @param y 文字的起点坐标
194 | */
195 | public void drawNumericText(Canvas canvas, LiveBitmap numbeBitmap, String msg, int x, int y) {
196 | if (!msg.matches("[0-9 ]*")){
197 | throw new IllegalArgumentException("drawable msg should contain only numbers and spaces. msg="+msg);
198 | }
199 | int len = msg.length();
200 | for (int i = 0; i < len; i++) {
201 | char character = msg.charAt(i);
202 |
203 | if (character == ' ') {
204 | x += 20; //留白
205 | continue;
206 | }
207 |
208 | int srcX = (character - '0') * 17;
209 | int srcWidth = 17; //字宽
210 |
211 | drawBitmap(canvas, numbeBitmap, x, y, srcX, 0, srcWidth, 21);
212 | x += srcWidth;
213 | }
214 | }
215 | /**
216 | * 绘制一般的文字。
217 | * @param msg 要绘制的消息
218 | * @param x 文字的起点坐标
219 | * @param y 文字的起点坐标
220 | */
221 | public void drawTextUsingAlpha(Canvas canvas, Player player, String msg,
222 | int x, int y) {
223 | if (canvas == null || msg == null) {
224 | return;
225 | }
226 | canvas.drawText(msg, x * scaleX, y * scaleY,
227 | getCorrespondingPaint(player));
228 | }
229 |
230 | public Point getCenter(LiveBitmap pixmap, float x, float y)
231 | {
232 | int centerX = (int) (x + pixmap.getRawWidth() / 2 + 0.5f);
233 | int centerY = (int) (y + pixmap.getRawHeight() / 2 + 0.5f);
234 | return new Point(centerX, centerY);
235 | }
236 |
237 | public float getScaleX() {
238 | return scaleX;
239 | }
240 |
241 | public float getScaleY() {
242 | return scaleY;
243 | }
244 |
245 | protected static final class AutoDecendAlphaPaint extends Paint{
246 | private int currentAlpha;
247 | private CountDownTimer alphaDecendTimer; //用于消减Alpha
248 |
249 | public AutoDecendAlphaPaint() {
250 | currentAlpha = 0;
251 | setAlpha(currentAlpha);
252 | }
253 |
254 | /**
255 | * @deprecated using {@link #getCurrentAlpha()} instead.
256 | */
257 | @Deprecated
258 | @Override
259 | public int getAlpha() {
260 | return super.getAlpha();
261 | }
262 |
263 | /**
264 | * @deprecated using {@link #setCurrentAlpha(int)} instead.
265 | */
266 | @Deprecated
267 | @Override
268 | public void setAlpha(int a) {
269 | super.setAlpha(a);
270 | }
271 |
272 | public int getCurrentAlpha(){
273 | return currentAlpha;
274 | }
275 |
276 | /**
277 | * Set current alpha value.
278 | * @param alpha The alpha value to set.
279 | */
280 | public void setCurrentAlpha(int alpha){
281 | if (alpha<0 || alpha > 255){
282 | throw new IllegalArgumentException("wrong alpha value " + alpha);
283 | }
284 | currentAlpha = alpha;
285 | setAlpha(currentAlpha);
286 | if (alphaDecendTimer!=null){
287 | alphaDecendTimer.cancel();
288 | }
289 | if (currentAlpha==0){
290 | return;
291 | }
292 | //大于0则开始衰减
293 | alphaDecendTimer = new CountDownTimer( 16*alpha/4,16) {
294 |
295 | @Override
296 | public void onTick(long millisUntilFinished) {
297 | //从255衰减到0需要64次,大约1s
298 | currentAlpha -= 4;
299 | if (currentAlpha<=0){
300 | currentAlpha=0;
301 | }
302 | setAlpha(currentAlpha);
303 | cancel();
304 | alphaDecendTimer = null;
305 | }
306 |
307 | @Override
308 | public void onFinish() {
309 | if (alphaDecendTimer != null){
310 | currentAlpha = 0;
311 | alphaDecendTimer.cancel();
312 | alphaDecendTimer = null;
313 | }
314 | }
315 | };
316 | alphaDecendTimer.start();
317 | }
318 | }
319 | }
320 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/res/GlobalSoundPool.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.res;
2 |
3 | import java.io.IOException;
4 |
5 | import com.mym.landlords.ui.Settings;
6 |
7 | import android.content.Context;
8 | import android.content.res.AssetFileDescriptor;
9 | import android.content.res.AssetManager;
10 | import android.media.AudioManager;
11 | import android.media.SoundPool;
12 |
13 | /**
14 | *
15 | * @author Muyangmin
16 | * @create 2015-3-16
17 | */
18 | public final class GlobalSoundPool {
19 |
20 | private static GlobalSoundPool instance;
21 | private SoundPool soundPool;
22 |
23 | private GlobalSoundPool(Context context){
24 | soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
25 | }
26 |
27 | public static GlobalSoundPool getInstance(Context context){
28 | if (instance==null){
29 | synchronized (GlobalSoundPool.class) {
30 | instance = new GlobalSoundPool(context);
31 | }
32 | }
33 | return instance;
34 | }
35 |
36 | /**
37 | * 加载音效。
38 | * @param context 上下文信息。
39 | * @param assets 要加载的音效文件名
40 | * @return 加载后得到的soundId,可用于播放。
41 | */
42 | protected final int loadSound(Context context, String assets){
43 | try {
44 | AssetManager am = context.getAssets();
45 | AssetFileDescriptor afd = am.openFd(assets);
46 | return soundPool.load(afd, 1);
47 | } catch (IOException e) {
48 | e.printStackTrace();
49 | }
50 | return -1;
51 | }
52 |
53 | /**
54 | * 播放指定的音效。
55 | * @param soundId 通过load方法加载得到的soundId。
56 | */
57 | public void playSound(int soundId){
58 | if (Settings.isVoiceEnabled()){
59 | soundPool.play(soundId, 0.5F, 0.5F, 0, 0, 1.0F);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/res/LiveBitmap.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.res;
2 |
3 | import java.io.IOException;
4 |
5 | import com.mym.util.BitmapUtil;
6 |
7 | import android.content.Context;
8 | import android.content.res.AssetManager;
9 | import android.graphics.Bitmap;
10 | import android.graphics.BitmapFactory;
11 |
12 | /**
13 | * 代表已被加载到内存中的Bitmap对象。
14 | * 通过 重载的scaleTo方法可以将位图尺寸进行重新缩放。
15 | * @author Muyangmin
16 | * @create 2015-3-15
17 | */
18 | public final class LiveBitmap {
19 | private Bitmap bitmap;
20 | private final int rawWidth;
21 | private final int rawHeight;
22 |
23 | // 强制使用工厂方法
24 | private LiveBitmap(Bitmap bitmap, int rawWidth, int rawHeight) {
25 | super();
26 | this.bitmap = bitmap;
27 | this.rawWidth = rawWidth;
28 | this.rawHeight = rawHeight;
29 | }
30 |
31 | /**
32 | * 按原有尺寸加载图片。
33 | * @param context 上下文信息。
34 | * @param assets 要加载的Bitmap文件名(必须是Assets文件)。
35 | * @return 加载得到的位图;如果失败,返回null。
36 | */
37 | protected static final LiveBitmap loadBitmap(Context context, String assets) {
38 | return loadBitmap(context, assets, 0, 0);
39 | }
40 |
41 | /**
42 | * 按原有尺寸加载图片,再按指定比例缩放。
43 | * @param context 上下文信息。
44 | * @param assets 要加载的Bitmap文件名(必须是Assets文件)。
45 | * @param scalex x缩放比。
46 | * @param scaley y缩放比。
47 | * @return 加载得到的位图;如果失败,返回null。
48 | */
49 | protected static final LiveBitmap loadBitmap(Context context, String assets,
50 | float scalex, float scaley) {
51 | LiveBitmap instance = null;
52 | AssetManager am = context.getAssets();
53 | try {
54 | Bitmap bitmap = BitmapFactory.decodeStream(am.open(assets));
55 | int rawWidth = bitmap.getWidth();
56 | int rawHeight = bitmap.getHeight();
57 | bitmap = BitmapUtil.scaleBitmap(bitmap, scalex, scaley);
58 | instance = new LiveBitmap(bitmap, rawWidth, rawHeight);
59 | } catch (IOException e) {
60 | e.printStackTrace();
61 | }
62 | return instance;
63 | }
64 |
65 | /**
66 | * 按原有尺寸加载图片,再缩放到指定尺寸。
67 | * @param context 上下文信息。
68 | * @param assets 要加载的Bitmap文件名(必须是Assets文件)。
69 | * @param width 目标宽度。
70 | * @param height 目标高度。
71 | * @return 加载得到的位图;如果失败,返回null。
72 | */
73 | protected static final LiveBitmap loadBitmap(Context context, String assets,
74 | int width, int height) {
75 | LiveBitmap instance = null;
76 | AssetManager am = context.getAssets();
77 | try {
78 | Bitmap bitmap = BitmapFactory.decodeStream(am.open(assets));
79 | int rawWidth = bitmap.getWidth();
80 | int rawHeight = bitmap.getHeight();
81 | if (width != 0 && height != 0) {
82 | bitmap = BitmapUtil.scaleBitmap(bitmap, width, height);
83 | }
84 | instance = new LiveBitmap(bitmap, rawWidth, rawHeight);
85 | } catch (IOException e) {
86 | e.printStackTrace();
87 | }
88 | return instance;
89 | }
90 |
91 | public Bitmap getBitmap() {
92 | return bitmap;
93 | }
94 |
95 | public int getRawWidth() {
96 | return rawWidth;
97 | }
98 |
99 | public int getRawHeight() {
100 | return rawHeight;
101 | }
102 |
103 | public int getWidth(){
104 | return bitmap.getWidth();
105 | }
106 |
107 | public int getHeight(){
108 | return bitmap.getHeight();
109 | }
110 |
111 | /**
112 | * 缩放图片到指定比例。
113 | * @param scalew 横向缩放比例
114 | * @param scaleh 纵向缩放比例
115 | */
116 | public void scaleTo(float scalew, float scaleh){
117 | bitmap = BitmapUtil.scaleBitmap(bitmap, scalew, scaleh);
118 | }
119 |
120 | /**
121 | * 缩放图片到指定尺寸。
122 | * @param width 目标宽度
123 | * @param height 目标高度
124 | */
125 | public void scaleTo(int width, int height){
126 | bitmap = BitmapUtil.scaleBitmap(bitmap, width, height);
127 | }
128 |
129 | @Override
130 | public String toString() {
131 | StringBuilder builder = new StringBuilder();
132 | builder.append("LiveBitmap [getRawWidth()=").append(getRawWidth())
133 | .append(", getRawHeight()=").append(getRawHeight())
134 | .append(", getWidth()=").append(getWidth())
135 | .append(", getHeight()=").append(getHeight()).append("]");
136 | return builder.toString();
137 | }
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/test/SpecifiedHandCardGen.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.test;
2 |
3 | import java.util.ArrayList;
4 |
5 | import com.mym.landlords.card.Card;
6 | import com.mym.landlords.card.CardSuit;
7 |
8 | public class SpecifiedHandCardGen {
9 | //根据指定的点数生成手牌。
10 | public static ArrayList createHandCards(int... cardsValue){
11 | if (cardsValue.length!=17){
12 | throw new IllegalArgumentException("length :"+cardsValue.length);
13 | }
14 | ArrayList cards = new ArrayList<>();
15 | for (int value:cardsValue){
16 | cards.add(new Card(value>=Card.CARD_VALUE_JOKER_S?CardSuit.Joker:CardSuit.Spade, value));
17 | }
18 | return cards;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/ui/LoadingActivity.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.ui;
2 |
3 | import java.lang.ref.WeakReference;
4 |
5 | import com.mym.landlords.R;
6 | import com.mym.landlords.res.Assets;
7 | import com.mym.landlords.res.Assets.LoadingProgressListener;
8 |
9 | import android.app.Activity;
10 | import android.content.Intent;
11 | import android.os.AsyncTask;
12 | import android.os.Bundle;
13 | import android.os.Handler;
14 | import android.os.Message;
15 | import android.util.Log;
16 | import android.view.View;
17 | import android.widget.CompoundButton;
18 | import android.widget.CompoundButton.OnCheckedChangeListener;
19 | import android.widget.ProgressBar;
20 | import android.widget.ToggleButton;
21 |
22 | public class LoadingActivity extends Activity {
23 |
24 | protected static final String LOG_TAG = "LoadingActivity";
25 |
26 | private ProgressBar progressBar;
27 |
28 | private AsyncTask loadTask = new AsyncTask() {
29 | protected Void doInBackground(Void... params) {
30 | Assets.loadAssets(LoadingActivity.this, new LoadingProgressListener() {
31 |
32 | @Override
33 | public void onProgressChanged(int progress) {
34 | publishProgress(progress);
35 | }
36 |
37 | @Override
38 | public void onLoadCompleted() {
39 | //do nothing.在onPostExecute中执行操作。
40 | }
41 | });
42 | return null;
43 | };
44 |
45 | protected void onProgressUpdate(Integer... values) {
46 | progressBar.setProgress((int)values[0]);
47 | };
48 |
49 | protected void onPostExecute(Void result) {
50 | Log.d(LOG_TAG, "loading completed.");
51 | //隐藏进度条,并将按钮组展示出来。
52 | progressBar.setVisibility(View.INVISIBLE);
53 | findViewById(R.id.loading_ll_btns).setVisibility(View.VISIBLE);
54 | };
55 | };
56 |
57 | private Handler handler = new LoadHandler(new WeakReference(this));
58 |
59 | @Override
60 | protected void onCreate(Bundle savedInstanceState) {
61 | super.onCreate(savedInstanceState);
62 | setContentView(R.layout.activity_loading);
63 | progressBar = (ProgressBar) findViewById(R.id.loading_prg);
64 | ToggleButton tgb = (ToggleButton) findViewById(R.id.loading_tgb_voice);
65 | tgb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
66 |
67 | @Override
68 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
69 | Settings.setVoiceEnabled(isChecked);
70 | }
71 | });
72 | Settings.setVoiceEnabled(tgb.isChecked());
73 | handler.sendEmptyMessageDelayed(LoadHandler.MSG_START_LOADING, 1000);//Splash显示时间
74 | }
75 |
76 | private static final class LoadHandler extends Handler{
77 | private static final int MSG_START_LOADING = 1;
78 | private WeakReference wk;
79 |
80 | public LoadHandler(WeakReference wk) {
81 | super();
82 | this.wk = wk;
83 | }
84 | @Override
85 | public void handleMessage(Message msg) {
86 | super.handleMessage(msg);
87 | LoadingActivity activity = wk.get();
88 | if (msg.what==MSG_START_LOADING && activity!=null){
89 | activity.loadTask.execute();
90 | }
91 | }
92 | }
93 |
94 | public void onClick(View v){
95 | switch (v.getId()) {
96 | case R.id.loading_btn_startgame:
97 | startActivity(MainActivity.getIntent(LoadingActivity.this));
98 | finish();
99 | break;
100 | case R.id.loading_btn_share:
101 | Intent intent=new Intent(Intent.ACTION_SEND);
102 | intent.setType("text/plain");
103 | intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.default_share_title));
104 | intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.default_share_content));
105 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
106 | startActivity(Intent.createChooser(intent, getString(R.string.default_share_title)));
107 | break;
108 | default:
109 | break;
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/ui/Settings.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.ui;
2 |
3 | /**
4 | * @author Muyangmin
5 | */
6 | public final class Settings {
7 | private static boolean isVoiceEnabled;
8 |
9 | public static boolean isVoiceEnabled() {
10 | return isVoiceEnabled;
11 | }
12 |
13 | public static void setVoiceEnabled(boolean isEnabled) {
14 | isVoiceEnabled = isEnabled;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/widget/BitmapButton.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.widget;
2 |
3 | import android.graphics.Canvas;
4 | import android.view.MotionEvent;
5 |
6 | import com.mym.landlords.res.Assets;
7 | import com.mym.landlords.res.GameGraphics;
8 | import com.mym.landlords.res.LiveBitmap;
9 |
10 | /**
11 | * 虚拟的按钮控件,使用Android的控件思想进行封装。
12 | * @author Muyangmin
13 | * @create 2015-3-17
14 | */
15 | public final class BitmapButton implements BitmapView{
16 |
17 | private onClickListener listener;
18 | private final GameGraphics graphics;
19 | private final int x; //x坐标起点
20 | private final int y; //y坐标起点
21 | private final LiveBitmap bkgNormal; //背景图片
22 | private final LiveBitmap bkgPressed; //按下背景图
23 | private final LiveBitmap bitmapNormal; //按钮图片
24 | private final LiveBitmap bitmapPressed; //按钮按下的图片
25 | private boolean isPressed; //是否已经被按下
26 |
27 | /**
28 | * 构造一个图片按钮,按钮不具备按下效果。
29 | * @param x 按钮的x轴起点坐标
30 | * @param y 按钮的y轴起点坐标
31 | * @param bitmap 按钮上显示的图片,不能为null
32 | */
33 | public BitmapButton(GameGraphics graphics, int x, int y, LiveBitmap bitmap) {
34 | this(graphics, x, y, bitmap, bitmap);
35 | }
36 |
37 | /**
38 | * 构造一个具备按下效果的图片按钮。
39 | * @param x 按钮的x轴起点坐标
40 | * @param y 按钮的y轴起点坐标
41 | * @param bitmapNormal 按钮上显示的图片,不能为null
42 | * @param bitmapPressed 按钮被按下时显示的图片,不能为null
43 | */
44 | public BitmapButton(GameGraphics graphics, int x, int y, LiveBitmap bitmapNormal,
45 | LiveBitmap bitmapPressed) {
46 | super();
47 | if (bitmapNormal==null || bitmapPressed==null){
48 | throw new NullPointerException("cannot construct a bitmap button using null bitmap.");
49 | }
50 | this.graphics = graphics;
51 | this.x = x;
52 | this.y = y;
53 | this.bitmapNormal = bitmapNormal;
54 | this.bitmapPressed = bitmapPressed;
55 | this.bkgNormal = Assets.getInstance().bitmapBtnBkg;
56 | this.bkgPressed = Assets.getInstance().bitmapBtnBkgPressed;
57 | }
58 |
59 | /**
60 | * 处理点击事件。
61 | * @param event 映射到基准屏幕的点击事件。
62 | * @return 如果消费了本次点击事件(通常表现为Listener被调用),则返回true。
63 | */
64 | public final boolean onTouch(MappedTouchEvent event){
65 | if (inBounds(event)){
66 | // Log.d("BitmapButton", "event in bounds caught:"+event.toString());
67 | if (event.getAction()==MotionEvent.ACTION_DOWN){
68 | isPressed = true;
69 | }
70 | else if (event.getAction()==MotionEvent.ACTION_UP){
71 | isPressed = false;
72 | if (listener!=null){
73 | listener.onClicked(this);
74 | return true;
75 | }
76 | }
77 | }
78 | return false;
79 | }
80 |
81 | @Override
82 | public void onPaint(Canvas canvas) {
83 | // bkgNormal
84 | // LiveBitmap btnBkg = Assets.getInstance().bitmapBtnBkg;
85 | LiveBitmap btnBkg = isPressed?bkgNormal:bkgPressed;
86 | graphics.drawBitmap(canvas, btnBkg, x, y);
87 | // graphics.drawBitmap(canvas, isPressed ? bitmapPressed : bitmapNormal, x, y);
88 | graphics.drawBitmapInParentCenter(canvas, isPressed ? bitmapPressed
89 | : bitmapNormal, graphics.getCenter(btnBkg, x, y));
90 | }
91 |
92 | /**
93 | * 判断指定的事件是否在自己的区域中。
94 | */
95 | private boolean inBounds(MappedTouchEvent event){
96 | // int width, height;
97 | // int width = bitmapNormal.getRawWidth();
98 | // int height = bitmapNormal.getRawHeight();
99 | int width = bkgNormal.getRawWidth();
100 | int height = bkgNormal.getRawHeight();
101 | if(event.x > x && event.x < x + width - 1 &&
102 | event.y > y && event.y < y + height - 1){
103 | return true;
104 | }
105 | else{
106 | return false;
107 | }
108 | }
109 |
110 | public interface onClickListener{
111 | /**
112 | * 控件被点击时调用。
113 | * @param btn 按钮自身的引用。
114 | */
115 | void onClicked(BitmapButton btn);
116 | }
117 |
118 | public void setOnClickListener(onClickListener listener) {
119 | this.listener = listener;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/widget/BitmapView.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.widget;
2 |
3 | import android.graphics.Canvas;
4 |
5 | /**
6 | * @author Muyangmin
7 | * @create 2015-3-17
8 | */
9 | public interface BitmapView {
10 | /** 绘制组件。 */
11 | void onPaint(Canvas canvas);
12 | }
13 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/widget/GameScreen.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.widget;
2 |
3 | import android.graphics.Canvas;
4 |
5 | import com.mym.landlords.res.GameGraphics;
6 |
7 | /**
8 | * @author Muyangmin
9 | * @create 2015-3-17
10 | */
11 | public interface GameScreen {
12 | /** 用于在界面重绘时展示本界面的必要元素。 */
13 | void updateUI(GameGraphics graphics, Canvas canvas);
14 | }
15 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/widget/GameView.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.widget;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.util.Log;
6 | import android.view.SurfaceHolder;
7 | import android.view.SurfaceView;
8 |
9 | import com.mym.landlords.res.Assets;
10 | import com.mym.landlords.res.GameGraphics;
11 |
12 | /**
13 | * 主游戏界面绘制控件。
14 | * @author Muyangmin
15 | * @create 2015-3-17
16 | */
17 | public class GameView extends SurfaceView implements RedrawableView,
18 | SurfaceHolder.Callback {
19 |
20 | private static final String LOG_TAG = "GameView";
21 |
22 | private SurfaceHolder holder;
23 | private GameGraphics graphics;
24 | private GameScreen gamescreen;
25 | private RenderThread renderThread; //渲染线程。为加入Activity的生命周期支持,在这里不赋值
26 |
27 | public GameView(Context context, GameGraphics graphics, GameScreen listener) {
28 | super(context);
29 | if (graphics==null){
30 | throw new NullPointerException("graphics object cannot be null.");
31 | }
32 | this.graphics = graphics;
33 | this.holder = getHolder();
34 | this.holder.addCallback(this);
35 | this.gamescreen = listener;
36 | }
37 |
38 | @Override
39 | public void redraw() {
40 | Canvas canvas = null;
41 | try {
42 | canvas = holder.lockCanvas();
43 | if (canvas==null){
44 | Log.w(LOG_TAG, "cancel drawing on null canvas.");
45 | return ;
46 | }
47 | graphics.drawBitmap(canvas, Assets.getInstance().bkgGameTable, 0, 0);
48 | if (gamescreen!=null){
49 | gamescreen.updateUI(graphics, canvas);
50 | }
51 | } catch (Exception e) {
52 | e.printStackTrace();
53 | } finally {
54 | //强制释放Canvas,保证下帧正确运行
55 | if (canvas != null) {
56 | holder.unlockCanvasAndPost(canvas);
57 | }
58 | }
59 | }
60 |
61 | @Override
62 | public void surfaceChanged(SurfaceHolder holder, int format, int width,
63 | int height) {
64 | }
65 |
66 | @Override
67 | public void surfaceCreated(SurfaceHolder holder) {
68 | Log.d(LOG_TAG, "surfaceCreated.");
69 | this.holder = getHolder();
70 | renderThread = new RenderThread();
71 | renderThread.start();
72 | }
73 |
74 | @Override
75 | public void surfaceDestroyed(SurfaceHolder holder) {
76 | Log.d(LOG_TAG, "surfaceDestroyed.");
77 | if (renderThread != null && renderThread.isAlive()) {
78 | try {
79 | // Important:join方法会等待线程完成工作后再结束线程,但如果是死循环则永远不会结束。
80 | renderThread.stopThread();
81 | renderThread.join(); // 销毁线程。
82 | } catch (Exception e) {
83 |
84 | }
85 | }
86 | renderThread = null;
87 | }
88 |
89 | private final class RenderThread extends Thread{
90 |
91 | private boolean hasStopped;
92 |
93 | public RenderThread() {
94 | super("RenderThread");
95 | hasStopped = false;
96 | }
97 |
98 | protected synchronized final void stopThread(){
99 | hasStopped = true;
100 | }
101 |
102 | public void run() {
103 | long startTime, endTime, freeTime;
104 | while (!hasStopped) {
105 | try {
106 | startTime = System.currentTimeMillis();
107 | redraw();
108 | endTime = System.currentTimeMillis();
109 | freeTime = 17 - (endTime - startTime);
110 | if (freeTime > 0) {
111 | sleep(freeTime);// 稳定帧频
112 | }
113 | } catch (Exception e) {
114 | Log.w(LOG_TAG, "exception while rendering:" + e.getMessage());
115 | }
116 | }
117 | };
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/widget/MappedTouchEvent.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.widget;
2 |
3 | import android.view.MotionEvent;
4 |
5 | /**
6 | * 将触摸屏实际捕捉到的TouchEvent映射回基本屏幕(800*400)尺寸上的坐标。
7 | * @author Muyangmin
8 | * @create 2015-3-17
9 | */
10 | public final class MappedTouchEvent {
11 | public final int x;
12 | public final int y;
13 | private final MotionEvent originEvent;
14 | private static Mapper mapper;
15 |
16 | /**
17 | * 初始化转换器。
18 | * @param scaleX X轴缩放比例
19 | * @param scaleY Y轴缩放比例。
20 | */
21 | public static final void initMapper(float scaleX, float scaleY){
22 | mapper = new Mapper(scaleX, scaleY);
23 | }
24 |
25 | public static MappedTouchEvent translateEvent(MotionEvent event){
26 | if (mapper==null){
27 | throw new RuntimeException("must call initMapper() as early as possible.");
28 | }
29 | return mapper.translateEvent(event);
30 | }
31 |
32 | private MappedTouchEvent(int x, int y, MotionEvent event) {
33 | super();
34 | this.x = x;
35 | this.y = y;
36 | this.originEvent = event;
37 | }
38 |
39 | public int getAction(){
40 | return originEvent.getAction();
41 | }
42 |
43 |
44 | private static final class Mapper{
45 | private float scaleX;
46 | private float scaleY;
47 |
48 | public Mapper(float scaleX, float scaleY) {
49 | super();
50 | this.scaleX = scaleX;
51 | this.scaleY = scaleY;
52 | }
53 |
54 | public MappedTouchEvent translateEvent(MotionEvent event){
55 | int x = (int) (event.getX()/scaleX);
56 | int y = (int) (event.getY()/scaleY);
57 | MappedTouchEvent map = new MappedTouchEvent(x, y, event);
58 | return map;
59 | }
60 | }
61 |
62 |
63 | @Override
64 | public String toString() {
65 | StringBuilder builder = new StringBuilder();
66 | builder.append("MappedTouchEvent [x=").append(x).append(", y=")
67 | .append(y).append(", originEvent=").append(originEvent)
68 | .append("]");
69 | return builder.toString();
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/landlords/widget/RedrawableView.java:
--------------------------------------------------------------------------------
1 | package com.mym.landlords.widget;
2 |
3 | /**
4 | * 用于实现游戏的虚拟屏幕。
5 | *
6 | *
7 | * 本游戏为棋牌类游戏,并不需要刷帧,设计思路为被动式刷新。定义接口 {@link RedrawableView} 供View或SurfaceView进行实现,
8 | * 展示界面的Activity通过使用按钮的事件回调改变数据和逻辑,并调用 {@link #redraw()}方法执行UI的重绘。
9 | *
10 | *
11 | * @author Muyangmin
12 | * @create 2015-3-17
13 | */
14 | public interface RedrawableView {
15 | /**
16 | * 用于客户端请求重绘UI。
17 | */
18 | void redraw();
19 | }
20 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/util/BitmapUtil.java:
--------------------------------------------------------------------------------
1 | package com.mym.util;
2 |
3 | import java.io.File;
4 | import java.io.FileNotFoundException;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 |
8 | import android.annotation.TargetApi;
9 | import android.graphics.Bitmap;
10 | import android.graphics.Matrix;
11 | import android.graphics.Bitmap.CompressFormat;
12 | import android.os.Build;
13 |
14 | /**
15 | * 处理Bitmap相关的操作(旋转、缩放,etc.)。
16 | *
17 | * @author Muyangmin
18 | * @create 2015-2-12
19 | */
20 | public final class BitmapUtil {
21 | /**
22 | * 计算Bitmap占用的内存大小,以字节为单位。
23 | * @param bitmap 目标位图。
24 | * @return 返回Bitmap所占空间的大小。
25 | */
26 | @TargetApi(19)
27 | public static final int getBitmapSize(Bitmap bitmap){
28 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){ //API 19
29 | return bitmap.getAllocationByteCount();
30 | }
31 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1){//API 12
32 | return bitmap.getByteCount();
33 | }
34 | return bitmap.getRowBytes() * bitmap.getHeight(); //earlier version
35 | }
36 |
37 | /**
38 | * 缩放图片到指定尺寸。
39 | * @param bitmap 原图
40 | * @param width 目标宽度
41 | * @param height 目标高度
42 | * @return 缩放后的位图
43 | */
44 | public static final Bitmap scaleBitmap(Bitmap bitmap, int width, int height) {
45 | if (width <=0 || height <=0 ){
46 | throw new IllegalArgumentException("target size must be positive!");
47 | }
48 | int w = bitmap.getWidth();
49 | int h = bitmap.getHeight();
50 | Matrix matrix = new Matrix();
51 | float scaleWidth = ((float) width / w);
52 | float scaleHeight = ((float) height / h);
53 | matrix.postScale(scaleWidth, scaleHeight);// 利用矩阵进行缩放不会造成内存溢出
54 | Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
55 | return newbmp;
56 | }
57 |
58 | /**
59 | * 缩放图片到指定比例。
60 | * @param bitmap 原图
61 | * @param scalew 横向缩放比例
62 | * @param scaleh 纵向缩放比例
63 | * @return 缩放后的位图
64 | */
65 | public static final Bitmap scaleBitmap(Bitmap bitmap, float scalew,
66 | float scaleh) {
67 | if (scalew <=0 || scaleh <=0 ){
68 | throw new IllegalArgumentException("scale rate must be positive!");
69 | }
70 | Matrix matrix = new Matrix();
71 | matrix.postScale(scalew, scaleh);// 利用矩阵进行缩放不会造成内存溢出
72 | Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
73 | bitmap.getHeight(), matrix, true);
74 | return newbmp;
75 | }
76 | /**
77 | * 保存Bitmap到文件,将采用无损格式保存(PNG格式)。
78 | * @param path 文件的绝对路径。如果目标文件不存在,则创建之。
79 | * @param bitmap 要保存的Bitmap。
80 | */
81 | public static void saveBitmapToFile(String path, Bitmap bitmap) {
82 | saveBitmapToFile(path, bitmap, CompressFormat.PNG, 100);
83 | }
84 |
85 | /**
86 | * 保存Bitmap到文件,将使用指定的压缩比率和格式。
87 | * @param path 文件的绝对路径。如果目标文件不存在,则创建之。
88 | * @param bitmap 要保存的Bitmap。
89 | * @param format 要使用的压缩格式。
90 | * @param quality 图片质量(0=最小压缩,100=最大质量)。
91 | */
92 | public static void saveBitmapToFile(String path, Bitmap bitmap,
93 | CompressFormat format, int quality) {
94 | File dir = new File(path).getParentFile();
95 | if (!dir.exists()) {
96 | dir.mkdirs();
97 | }
98 | File photoFile = new File(path); // 在指定路径下创建文件
99 | FileOutputStream fos = null;
100 | try {
101 | fos = new FileOutputStream(photoFile);
102 | if (bitmap != null) {
103 | if (bitmap.compress(format, quality, fos)) {
104 | fos.flush();
105 | }
106 | }
107 | } catch (FileNotFoundException e) {
108 | photoFile.delete();
109 | e.printStackTrace();
110 | } catch (IOException e) {
111 | photoFile.delete();
112 | e.printStackTrace();
113 | } finally {
114 | try {
115 | if (fos!=null){
116 | fos.close();
117 | }
118 | } catch (IOException e) {
119 | e.printStackTrace();
120 | }
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/util/LangUtils.java:
--------------------------------------------------------------------------------
1 | package com.mym.util;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * 一些语言上的快捷方法。
7 | * @author Muyangmin
8 | * @create 2015-4-8
9 | */
10 | public final class LangUtils {
11 | /**
12 | * 创建一个ArrayList,保证该列表的迭代顺序和参数顺序一样。
13 | * @param args 需要加入ArrayList的元素。必须注意元素的类型,否则可能导致堆污染。
14 | * @return 返回装有这些元素的列表。
15 | */
16 | @SafeVarargs
17 | public static final ArrayList createList(T... args){
18 | ArrayList list = new ArrayList<>();
19 | for (T element:args){
20 | list.add(element);
21 | }
22 | return list;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Landlords/src/com/mym/util/PollingThread.java:
--------------------------------------------------------------------------------
1 | package com.mym.util;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * 使用轮询方式进行工作的一种线程,启动线程后会不断执行 {@link #action()}方法。 通过
7 | * {@link #requestStopThread()}方法来结束线程 。
8 | *
9 | * 推荐使用 {@link #PollingThread(String)}构造器,以使这种线程易于识别。
10 | *
11 | *
12 | * @author Muyangmin
13 | * @create 2015-3-21
14 | */
15 | public abstract class PollingThread extends Thread {
16 |
17 | private boolean stopRequested;
18 | private long interval;
19 |
20 | /**
21 | * 构造一个轮询线程
22 | * @param threadName 线程名称。
23 | * @param interval 设置线程每两次执行之间的间隔时间(单位:ms)。
24 | * 如果该值为0,线程不会做任何间隔处理。
25 | */
26 | public PollingThread(String threadName, long interval) {
27 | super(threadName);
28 | this.interval = interval;
29 | }
30 |
31 | @Override
32 | public final void run() {
33 | while (!stopRequested) {
34 | try {
35 | action();
36 | if (interval>0){
37 | sleep(interval);
38 | }
39 | } catch (Exception e) {
40 | //This abstract class just ignore exception.
41 | onExceptionOccured(e);
42 | }
43 | action();
44 | }
45 | }
46 |
47 | /**
48 | * 线程具体需要循环执行的操作。
49 | */
50 | protected abstract void action();
51 |
52 | /**
53 | * 当线程执行出现异常后调用该方法。
54 | */
55 | protected void onExceptionOccured(Exception e){
56 | Log.d(getName(), "polling exception:" + e.getMessage());
57 | }
58 |
59 | /**
60 | * 提交终止线程的操作。线程会在最后一次操作完成后销毁。
61 | */
62 | public synchronized final void requestStopThread() {
63 | stopRequested = true;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------