【アルゴリズム】マージャン聴牌アルゴリズム、StreamAPIを利用して、スレッドプールを使って聴牌を探し、胡牌を判断する
効果
判断がでたらめである.
看板をさがす
マルチカード型の胡牌探し
今回の聴牌アルゴリズムはJava 8のStreamAPIの新しい特性を採用して牌組集合を処理し、従来の集合遍歴より性能が大幅に向上し、並列もサポートされている.これはマルチスレッドに良いサポートを提供している.また、牌組の符号化は集合工場を採用しており、下層省は修正の機能を省き、作成後は修正できず遍歴するしかなく、しかも付与時に便利である.
プロジェクトのソース、jarファイル、githubに行って取得してください.https://github.com/ZDG-Kinlon/MahjongCheckHuUtil
デッキクラス
Deck.JAvaはデッキを変換するためだけで、計算と出力の切り替えが便利です
胡牌の類を判断する
CheckHu.JAvaはStreamAPIを利用してカードを処理し、まず7対の子、国士無双という特殊なカード型を判断し、成立しなければ3 n*2のカード型かどうかを検出するしかない.
3 nの牌型は刻牌とチェーン牌の2種類があり、刻牌は3枚の同じ牌で、チェーン牌は連続した同色牌である.
nは最大で4しかないので、札を外すときは最大4層のネストを使うことができ、全部で16種類の組み合わせで、札型が胡札であれば、絶対に最後の1枚まで分解できるものがあります.
カードを取り外す時、もしテストカードの取り外しに失敗したら、ロールバックして元に戻して、次の取り外しに影響しません.番号は10、20、30、40番札がないので、間接的に色を区別する役割を果たして、チェーンカードを取り外す時の色をまたがる問題を避けました.
Java 8に導入されたStreamAPIは、集合の処理において大きく強化されており、集合中の要素をExcelテーブルの各列のセルとして抽象化することができ、後続の一連の点方法は、テーブルの最初の行にあるフィルタリングと統計機能であり、容易に集合の処理を完了することができる.
Java 9が導入した集合ファクトリof()メソッドは,変更不可能でクエリーのみの集合を迅速に生成でき,効率が大幅に向上した.
聴牌の種類を探しています
FindHu.JAvaはスレッドプールを使用して、すべてのカードの可能性を遍歴し、測定対象カードと組み合わせて完全なカード型にし、スレッドに渡して実行し、最後に結果がtrueのカード番号の集合に戻る
複数のデッキの聴牌類を探しています
FindHus.JAvaは同様にスレッドプールを用いて,受信した各カードグループに対して聴牌のクラスを探す方法を呼び出し,マルチタスクを同時に実行し,スレッドプールの氾濫を避けるためにデフォルト設定を最大4つとし,実際のハードウェア状況に応じて調整することを提案した.
テスト例
胡牌判断方法1
胡牌判断方法2
トランプの聞き方を探す
マルチデッキの聴牌を探しています
判断がでたらめである.
看板をさがす
マルチカード型の胡牌探し
今回の聴牌アルゴリズムはJava 8のStreamAPIの新しい特性を採用して牌組集合を処理し、従来の集合遍歴より性能が大幅に向上し、並列もサポートされている.これはマルチスレッドに良いサポートを提供している.また、牌組の符号化は集合工場を採用しており、下層省は修正の機能を省き、作成後は修正できず遍歴するしかなく、しかも付与時に便利である.
プロジェクトのソース、jarファイル、githubに行って取得してください.https://github.com/ZDG-Kinlon/MahjongCheckHuUtil
デッキクラス
Deck.JAvaはデッキを変換するためだけで、計算と出力の切り替えが便利です
package cn.mahjong;
import java.util.ArrayList;
import java.util.List;
public class Deck {
//
public static final List DECK_NO = List.of(
11, 12, 13, 14, 15, 16, 17, 18, 19,
21, 22, 23, 24, 25, 26, 27, 28, 29,
31, 32, 33, 34, 35, 36, 37, 38, 39,
41, 42, 43, 44, 45, 46, 47
);
//
private static final List DECK_STR = List.of(
" ", " ", " ", " ", " ", " ", " ", " ", " ",
" ", " ", " ", " ", " ", " ", " ", " ", " ",
" ", " ", " ", " ", " ", " ", " ", " ", " ",
" ", " ", " ", " ", " ", " ", " "
);
/**
*
* @param num
* @return
* @throws IndexOutOfBoundsException
*/
public static String getDeckByStr(int num)
throws IndexOutOfBoundsException {
return DECK_STR.get(DECK_NO.indexOf(num));
}
/**
*
* @param str
* @return
* @throws IndexOutOfBoundsException
*/
public synchronized static Integer getDeckByNO(String str)
throws IndexOutOfBoundsException {
return DECK_NO.get(DECK_STR.indexOf(str));
}
/**
*
* @param stringList
* @return
* @throws IndexOutOfBoundsException
*/
public static List toDeckNO(List stringList)
throws IndexOutOfBoundsException {
List out = new ArrayList<>();
stringList.forEach(s -> out.add(getDeckByNO(s)));
return out;
}
/**
*
* @param intList
* @return
* @throws IndexOutOfBoundsException
*/
public static List toDeckStr(List intList)
throws IndexOutOfBoundsException {
List out = new ArrayList<>();
intList.forEach(s -> out.add(getDeckByStr(s)));
return out;
}
}
胡牌の類を判断する
CheckHu.JAvaはStreamAPIを利用してカードを処理し、まず7対の子、国士無双という特殊なカード型を判断し、成立しなければ3 n*2のカード型かどうかを検出するしかない.
3 nの牌型は刻牌とチェーン牌の2種類があり、刻牌は3枚の同じ牌で、チェーン牌は連続した同色牌である.
nは最大で4しかないので、札を外すときは最大4層のネストを使うことができ、全部で16種類の組み合わせで、札型が胡札であれば、絶対に最後の1枚まで分解できるものがあります.
カードを取り外す時、もしテストカードの取り外しに失敗したら、ロールバックして元に戻して、次の取り外しに影響しません.番号は10、20、30、40番札がないので、間接的に色を区別する役割を果たして、チェーンカードを取り外す時の色をまたがる問題を避けました.
Java 8に導入されたStreamAPIは、集合の処理において大きく強化されており、集合中の要素をExcelテーブルの各列のセルとして抽象化することができ、後続の一連の点方法は、テーブルの最初の行にあるフィルタリングと統計機能であり、容易に集合の処理を完了することができる.
Java 9が導入した集合ファクトリof()メソッドは,変更不可能でクエリーのみの集合を迅速に生成でき,効率が大幅に向上した.
package cn.mahjong;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.BooleanSupplier;
import java.util.stream.Collectors;
public class CheckHu implements Callable<Map<Integer, Boolean>> {
private Integer newDeck;//
private List<Integer> checkDeckList;//
private Map<Integer, Long> map;//
public CheckHu(List<Integer> checkDeckList) {
this.checkDeckList = checkDeckList;
}
public CheckHu(List<Integer> checkDeckList, Integer newDeck) {
this.newDeck = newDeck;
this.checkDeckList = checkDeckList;
}
/**
*
*
* @return
* @throws Exception
*/
@Override
public Map<Integer, Boolean> call() throws Exception {
return Map.of(newDeck, isHu());
}
/**
*
*
* @return
*/
public boolean isHu() {
return init.getAsBoolean() && (isQiDuiZi.getAsBoolean() ||
isGuoShiWuShuang.getAsBoolean() || is3n2.getAsBoolean());
}
//
private BooleanSupplier init = () -> {
List<Integer> list = new ArrayList<>(this.checkDeckList);
if (newDeck != null) list.add(newDeck);// ,
Collections.sort(list);// ,
this.checkDeckList = list;
//
this.map = list.stream()
.collect(Collectors.groupingBy(integer -> integer, Collectors.counting()));
return !check4count();// 4
};
/**
* 4
*
* @return
*/
private boolean check4count() {
//
return this.map.values().stream()
.max(Comparator.comparing(count -> count)).get().intValue() > 4;
}
// Lambda , 7 , 2 7 ,
private BooleanSupplier isQiDuiZi = () -> this.map.keySet().size() == 7 &&
this.map.values().stream().filter(count -> count == 2).count() == 7;
// Lambda , 13 , 40 , 40 13
private BooleanSupplier isGuoShiWuShuang = () -> this.map.keySet().size() == 13 &&
this.map.keySet().stream()
.filter(a -> a / 10 == 4 || a % 10 == 9 || a % 10 == 1).count() == 13;
// 3n+2 Lambda
private BooleanSupplier is3n2 = () -> {
int count = checkDeckList.size();// 3n+2, 2 14
if (count >= 2 && count <= 14 && (count - 2) % 3 == 0) {
List<Integer> duiDeckList = new ArrayList<>();// (2) ,
this.map.forEach((a, b) -> {
if (b > 1) duiDeckList.add(a);
});
for (Integer a : duiDeckList) {//
List<Integer> testList = new LinkedList<>(this.checkDeckList);
testList.remove(a);// , 3n
testList.remove(a);
if (_A(_A(_A(_A(testList)))).size() == 0) return true;//
if (_A(_A(_A(_B(testList)))).size() == 0) return true;//
if (_A(_A(_B(_A(testList)))).size() == 0) return true;//
if (_A(_A(_B(_B(testList)))).size() == 0) return true;//
if (_A(_B(_A(_A(testList)))).size() == 0) return true;//
if (_A(_B(_A(_B(testList)))).size() == 0) return true;//
if (_A(_B(_B(_A(testList)))).size() == 0) return true;//
if (_A(_B(_B(_B(testList)))).size() == 0) return true;//
if (_B(_A(_A(_A(testList)))).size() == 0) return true;//
if (_B(_A(_A(_B(testList)))).size() == 0) return true;//
if (_B(_A(_B(_A(testList)))).size() == 0) return true;//
if (_B(_A(_B(_B(testList)))).size() == 0) return true;//
if (_B(_B(_A(_A(testList)))).size() == 0) return true;//
if (_B(_B(_A(_B(testList)))).size() == 0) return true;//
if (_B(_B(_B(_A(testList)))).size() == 0) return true;//
if (_B(_B(_B(_B(testList)))).size() == 0) return true;//
}
}
return false;// 4 3n , , 3n+2
};
/**
*
*
* @param list
* @return
*/
private List<Integer> _A(List<Integer> list) {
for (Integer integer : list) {//
List<Integer> testList = new LinkedList<>(list);//
// , ,
if (breakDeckList(testList, integer, integer, integer)) return testList;
}
return list;// ,
}
/**
*
*
* @param list
* @return
*/
private List<Integer> _B(List<Integer> list) {
for (Integer integer : list) {//
if (integer / 10 == 4) continue;//
List<Integer> testList = new LinkedList<>(list);//
// , ,
if (breakDeckList(testList, integer, integer + 1, integer + 2)) return testList;
}
return list;
}
/**
*
*
* @param list
* @param i1
* @param i2
* @param i3
* @return
*/
private boolean breakDeckList(List<Integer> list, Integer i1, Integer i2, Integer i3) {
// , false
return list.remove(i1) && list.remove(i2) && list.remove(i3);
}
}
聴牌の種類を探しています
FindHu.JAvaはスレッドプールを使用して、すべてのカードの可能性を遍歴し、測定対象カードと組み合わせて完全なカード型にし、スレッドに渡して実行し、最後に結果がtrueのカード番号の集合に戻る
package cn.mahjong;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class FindHu implements Callable<Map<List<Integer>, List<Integer>>> {
private List<Integer> integerList;//
public FindHu(List<Integer> integerList) {
this.integerList = integerList;
}
/**
*
* @return
*/
public List<Integer> findHu() {
List<CheckHu> checkHuList = new ArrayList<>();//
List<Integer> allDeck = Deck.DECK_NO;//
// ,
allDeck.forEach(integer -> checkHuList.add(new CheckHu(this.integerList, integer)));
// ,
ExecutorService threadPool = Executors.newFixedThreadPool(allDeck.size());
List<Future<Map<Integer, Boolean>>> futures = new ArrayList<>();//
try {
// , call ,
checkHuList.forEach(checkHu -> futures.add(threadPool.submit(checkHu)));
} finally {
threadPool.shutdown();//
}
List<Integer> out = new ArrayList<>();//
futures.forEach(f -> {
try {
f.get().forEach((a, b) -> {
if (b) out.add(a);// , true
});
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
return out;//
}
/**
*
* @return
* @throws Exception
*/
@Override
public Map<List<Integer>, List<Integer>> call() throws Exception {
return Map.of(this.integerList, findHu());//key= ,value=
}
}
複数のデッキの聴牌類を探しています
FindHus.JAvaは同様にスレッドプールを用いて,受信した各カードグループに対して聴牌のクラスを探す方法を呼び出し,マルチタスクを同時に実行し,スレッドプールの氾濫を避けるためにデフォルト設定を最大4つとし,実際のハードウェア状況に応じて調整することを提案した.
package cn.mahjong;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class FindHus {
private List<List<Integer>> lists;
public FindHus(List<List<Integer>> lists) {
this.lists = lists;
}
/**
*
* @return
*/
public Map<List<Integer>, List<Integer>> findHu() {
ExecutorService threadPool = Executors.newFixedThreadPool(4);//
List<FindHu> findHus = new ArrayList<>();//
lists.forEach(list -> findHus.add(new FindHu(list)));// ,
List<Future<Map<List<Integer>, List<Integer>>>> futures = new ArrayList<>();//
try {
findHus.forEach(findHu -> futures.add(threadPool.submit(findHu)));// , call ,
} finally {
threadPool.shutdown();//
}
Map<List<Integer>, List<Integer>> out = new HashMap<>();//
futures.forEach(f -> {
try {
f.get().forEach(out::put);// ,
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
return out;
}
}
テスト例
胡牌判断方法1
public void checkDemo1() {
try {
//1. ,
List stringList = List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ");
//2. , IndexOutOfBoundsException
List integerList = Deck.toDeckNO(stringList);
//3.
CheckHu checkHu = new CheckHu(integerList);
//4.
boolean result = checkHu.isHu();
//5. , =true
System.out.println(result);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
胡牌判断方法2
public void checkDemo2() {
try {
//1. ,
List<String> stringList = List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ");
//2.
String checkDeck = " ";
//3. , IndexOutOfBoundsException
List<Integer> integerList = Deck.toDeckNO(stringList);
Integer checkDeckNO = Deck.getDeckByNO(checkDeck);
//4. , ,
CheckHu checkHu = new CheckHu(integerList, checkDeckNO);
//5.
boolean result = checkHu.isHu();
//6. , =true
System.out.println(result);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
トランプの聞き方を探す
public void findDemo1() {
try {
//1. ,
List<String> stringList = List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ");
//2. , IndexOutOfBoundsException
List<Integer> integerList = Deck.toDeckNO(stringList);
//3.
FindHu findHu = new FindHu(integerList);
//4.
List<Integer> result = findHu.findHu();
//5. , IndexOutOfBoundsException
List<String> out = Deck.toDeckStr(result);
//6.
out.forEach(str -> System.out.print(str + "\t"));
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
マルチデッキの聴牌を探しています
public void findDemo2() {
try {
//1. , ,
List<List<Integer>> lists = List.of(
// , 1
Deck.toDeckNO(List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ")),
// , 13
Deck.toDeckNO(List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ")),
//13 9
Deck.toDeckNO(List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ")),
//13 8
Deck.toDeckNO(List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ")),
//10 8
Deck.toDeckNO(List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ")),
//13 7
Deck.toDeckNO(List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ")),
//10 7
Deck.toDeckNO(List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ")),
//10 6
Deck.toDeckNO(List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ")),
//7 5
Deck.toDeckNO(List.of(" ", " ", " ", " ", " ", " ", " ")),
//10 4
Deck.toDeckNO(List.of(" ", " ", " ", " ", " ", " ", " ", " ", " ", " ")),
//7 4
Deck.toDeckNO(List.of(" ", " ", " ", " ", " ", " ", " ")),
//4 3
Deck.toDeckNO(List.of(" ", " ", " ", " "))
);
//2. ,
FindHus findHus = new FindHus(lists);
findHus.setnThreads(16);// , 4
//3.
Map<List<Integer>, List<Integer>> result = findHus.findHu();
//4. , ,
result.forEach((a, b) -> {
List<String> out1 = Deck.toDeckStr(a);
List<String> out2 = Deck.toDeckStr(b);
System.out.print(" :");
out1.forEach(e -> System.out.print(e + "\t"));
System.out.print("
:");
out2.forEach(e -> System.out.print(e + "\t"));
System.out.println("
");
});
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}