Я хочу заказать 13 игральных карт с порядком треф, бубен, червей и пик, последовательность 2,3,4,5,6,7,8,9,10,J,Q,K,A.
Например, 4C
означает «Клуб 4».
Я попробовал использовать приведенный ниже код, чтобы упорядочить их с помощью примера игрока на север, вот так
4H 5H 8C 8D 9H 9D AC 6S 5D 6D KD 10C AD
результат
8C AC 10C 5D 6D 8D 9D AD KD 4H 5H 9H 6S
Я ожидал результата
8C 10C AC 5D 6D 8D 9D KD AD 4H 5H 9H 6S
Вот код
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
String[] sTest = {"4H", "5H", "8C", "8D", "9H", "9D", "AC", "6S", "5D", "6D", "KD", "10C", "AD" };
for (int i=0;i<13;i++) {
System.out.print(sTest[i]+" ");
}
System.out.println();
System.out.println("Sorting");
ArrayList<String> tmp = sortEnd2Begin(sTest);
tmp.forEach((String myCard) ->{
System.out.print(myCard+" ");
});
System.out.println();
}
private static ArrayList<String> sortEnd2Begin(String[] myCards) {
ArrayList<String> myNewCards = new ArrayList<>();
myNewCards.add(myCards[0]);
for (int i=1;i<myCards.length;i++) {
// loop for unordered myCards
String card2Sort = myCards[i];
for (int j=0;j<myNewCards.size();j++) {
// loop for ordered myNewCards
// find until card2Sort equal or bigger then existing list
String cardIterate = myNewCards.get(j);
int iterMax = card2Sort.length();
if (card2Sort.length() > cardIterate.length()) {
iterMax = cardIterate.length();
}
int posCard = beforeOrAfter(card2Sort, cardIterate, 0, iterMax);
if (posCard==0 || posCard<0) {
myNewCards.add(j, card2Sort);
break;
} else {
// TODO
//System.out.println(j); // debug
if (j+1==myNewCards.size()) {
// end of list
myNewCards.add(card2Sort);
break;
}
}
}
}
return myNewCards;
}
// -1 wordA before wordB
// 0 wordA equal wordB
// 1 wordA after wordB
private static int beforeOrAfter(String wordA, String wordB, int iterX, int iterMax) {
//System.out.println(wordA+" "+wordB+" "+iterX+" "+iterMax);
int inA = wordA.length()-1-iterX;
int inB = wordB.length()-1-iterX;
char cA = wordA.charAt(inA);
char cB = wordB.charAt(inB);
int retValue = 0;
if (iterX+1==iterMax) {
// we reach max recursive {
//System.out.println(iterX+" "+iterMax+" "+cA+" "+cB); // debug
if (cA==cB) {
//System.out.println("Debug"); // debug
if (wordA.length()<wordB.length()) {
retValue = -1;
} else if (wordA.length()>wordB.length()) {
retValue = 1;
} else {
retValue = 0; // equal letters and length
}
} else if (cA<cB) {
//retValue = -1; // ori
if (wordA.length()>wordB.length()) {
retValue = 1;
} else {
retValue = -1;
}
} else if (cA>cB) {
retValue = 1; // ori
}
} else {
if (cA<cB) {
//System.out.println(cA+" < "+cB+" "+(cA < cB)); // debug
retValue = -1; // ori
} else if (cA>cB) {
//System.out.println(cA+" > "+cB+" "+(cA > cB)); // debug
retValue = 1; // ori
} else {
//System.out.println(cA+" = "+cB+" "+(cA == cB)); // debug
retValue = beforeOrAfter(wordA, wordB, iterX+1, iterMax);
}
}
return retValue;
}
}
Отредактировано 20240708
Функция sortEnd2Begin используется для сортировки массива строк и возврата ArrayList в виде отсортированного массива. функция sortEnd2Begin будет перебирать массив myCards и помещать его в новый список массивов в правильном порядке.
чтобы получить правильный порядок вставки или добавления списка массивов, я использую функцию beforeOrAfter, чтобы сравнить, что ниже, равно или выше. если он меньше или равен, он будет вставлен в начало или середину массива. если достигнут конец списка массивов, значение будет добавлено.
Функция beforeOrAfter будет сравнивать символы с конца слова, чтобы определить слово, равное, более низкое или более высокое, на основе таблицы ASCII. Функция beforeOrAfter будет рекурсивно работать до тех пор, пока не будет достигнута минимальная длина слова, и определит (возвращаемое значение) -1 (меньше), 0 (равно) или 1 (больше).
Функции sortEnd2Begin и beforeOrAfter работают аналогично сортировке имени файла в файловом менеджере, но в обратном порядке.
Мне трудно понять, что вы вообще пытаетесь здесь сделать. Хорошо, sortEnd2Begin
это Arrays.sort
. beforeOrAfter
— это Comparator
. Но зачем вам там использовать рекурсию? Не было бы намного проще сначала разделить карту на ранг и масть, а затем действовать только на основе этих значений?
в основном Arrays.sort(sTest, Comparator.comparing(Cards::suit).thenComparing(Cards::value))
, где String suit(String card)
возвращает масть, а Integer value(String card)
возвращает значение карты (например, с помощью помощника Map<String, Integer> values = Map.ofEntries(entry("2", 2), entry("3", 3),..., entry("10", 10), entry("J", 11), entry("Q", 12), entry("K", 13), entry("A", 14))
)
несмотря на то, что было бы лучше (IMO), если бы у нас были классы/записи для представления карт, мастей и значений.
Измените свои карты со слабо типизированных (небезопасных и подверженных ошибкам) на строго типизированные, и вы сможете легко повышать рейтинг по ходу дела. Фактически, игральные карты, iirc, являются шаблоном, используемым в руководстве по Java enum
.
Это руководство здесь
Я добавил объяснение того, как работают функции. Я использую Collections.shuffle(cards, rand) и Random rand = new Random().
Поскольку последняя буква обозначает масть, вы можете использовать ее для сортировки карт по мастям. Чтобы отсортировать по значениям, создайте список с допустимым порядком и сравните индекс ваших строк без последней буквы. Используйте интерфейс Компаратор, который упрощает цепочку сортировки и делает ваш код более читабельным.
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class Example {
public static void main(String[] args) {
List<String> order = List.of("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A");
String[] sTest = {"4H", "5H", "8C", "8D", "9H", "9D", "AC", "6S", "5D", "6D", "KD", "10C", "AD" };
// compare by last char
Comparator<String> bySuits = Comparator.comparingInt(s -> s.charAt(s.length()-1));
//compare by the index of substring as they appear in the list
Comparator<String> byValue = Comparator.comparingInt(s -> order.indexOf(s.substring(0, s.length()-1)));
System.out.println("Before sorting: " + Arrays.toString(sTest));
// compare by chaining the two comparators
Arrays.sort(sTest, bySuits.thenComparing(byValue));
System.out.println("After sorting: " + Arrays.toString(sTest));
}
}
Используйте enum
, чтобы определить колоду карт:
public final enum Card
{
TWO_OF_CLUBS( "2C" ),
THREE_OF_CLUBS( "3C" ),
…
ACE_OF_CLUBS( "AC" ),
TWO_OF_DIAMONDS( "2D" ),
…
TWO_OF_HEARTS( "2H" ),
…
ACE_OF_SPADES( "AS" );
private final String m_Code;
private Card( final String code )
{
m_Code = code;
}
public static final Card forCode( final String code ) throws IllegalArgumentException
{
final var retValue = stream( values() )
.filter( v -> v.m_Code.equals( code ) )
.findFirst()
.orElseThrow( () -> new IllegalArgumentException( Objects.toString( code ) );
return retValue();
}
public final String getCode() { return m_Code; }
@Override
public final String toString() { return getCode(); }
}
Сортировка теперь работает так:
final String[] sTest = {"4H", "5H", "8C", "8D", "9H", "9D", "AC", "6S", "5D", "6D", "KD", "10C", "AD" };
final var result = stream( sTest )
.map( Card::forCode )
.sorted()
.map( Card::getCode )
.toArray( String []::new );
Порядок сортировки определяется последовательностью, в которой вы определяете значения перечисления в Card
.
Забавным расширением может быть добавление кода Unicode в качестве символа к определению enum
:
public final enum Card
{
TWO_OF_CLUBS( "2C", "\u1F0D2" ),
THREE_OF_CLUBS( "3C", "\u1F0D2" ),
…
ACE_OF_CLUBS( "AC", "\uF0D1" ),
TWO_OF_DIAMONDS( "2D", "\uF0C2" ),
…
TWO_OF_HEARTS( "2H", "\uF0B2" ),
…
ACE_OF_SPADES( "AS", "\uF0A1" );
private final String m_Code;
private final String m_Symbol;
private Card( final String code )
{
m_Code = code;
m_Symbol = symbol;
}
public static final Card forCode( final String code ) throws IllegalArgumentException
{
final var retValue = stream( values() )
.filter( v -> v.m_Code.equals( code ) )
.findFirst()
.orElseThrow( () -> new IllegalArgumentException( Objects.toString( code ) );
return retValue();
}
public static final Card forSymbol( final String symbol ) throws IllegalArgumentException
{
final var retValue = stream( values() )
.filter( v -> v.m_Symbol.equals( symbol ) )
.findFirst()
.orElseThrow( () -> new IllegalArgumentException( Objects.toString( symbol ) );
return retValue();
}
public final String getCode() { return m_Code; }
public final String getSymbol() { return m_Symbol; }
@Override
public final String toString() { return getCode(); }
}
Это позволяет представлять карты следующим образом:
final String[] sTest = {"🂴", "🂵", "🃘", "🃈", "🂹", "🃉", "🃑", "🂦", "🃅", "🃆", "🃎", "🃚", "🃁" };
final var result = stream( sTest )
.map( Card::forSymbol )
.sorted()
.map( Card::getSymbol )
.toArray( String []::new );
Есть много способов сделать это. По моему мнению, самое простое — просто определить отсортированную колоду карт в виде строки и отсортировать ее по индексу, находящемуся в этой строке.
String[] sTest = {"4H", "5H", "8C", "8D", "9H", "9D", "AC",
"6S", "5D", "6D", "KD", "10C", "AD" };
String sortedDeck = """
2C3C4C5C6C7C8C9C10CJCQCKCAC\
2D3D4D5D6D7D8D9D10DJDQDKDAD\
2H3H4H5H6H7H8H9H10HJHQHKHAH\
2S3S4S5S6S7S8S9S10SJSQSKSAS\
""";
System.out.println(Arrays.toString(sTest)); // before sorting
Arrays.sort(sTest, Comparator.comparingInt(sortedDeck::indexOf));
System.out.println(Arrays.toString(sTest)); // after sorting
принты
[4H, 5H, 8C, 8D, 9H, 9D, AC, 6S, 5D, 6D, KD, 10C, AD]
[8C, 10C, AC, 5D, 6D, 8D, 9D, KD, AD, 4H, 5H, 9H, 6S]
Вышесказанное предполагает, что какой-то неизвестный элемент не проникнет в вашу колоду. Таким образом, предполагается, что индекс -1 не будет возвращен во время сортировки.
Вот колода, из которой вы получаете случайную комбинацию.
static String suit[] = {"C", "D", "H", "S"};
static String sym[] = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
static String card_suit(byte c)
{
return suit[c / 13];
}
static String card_sym(byte c)
{
return sym[c % 13];
}
static void shuffle(byte deck[])
{
ArrayList<Byte> list = new ArrayList<Byte>();
for (int i = 0; i < deck.length; i++) list.add(deck[i]);
Collections.shuffle(list);
for (int i = 0; i < deck.length; i++) deck[i] = list.get(i);
}
static void print(byte hand[])
{
for (int i = 0; i < hand.length; i++)
{
byte c = hand[i];
String card = card_sym(c) + card_suit(c);
System.out.print(card + " ");
}
System.out.println();
}
public static void main(String[] args)
{
byte deck[] = new byte[52];
for (int i = 0; i < deck.length; i++) deck[i] = (byte)i;
print(deck);
shuffle(deck);
print(deck);
Arrays.sort(deck);
print(deck);
}
Наборы C, D, H и S указаны в порядке ASCII.
Значение сюиты 2 – 10, J, Q, K и A – нет. Я переназначаю 10 на A, J на B, Q на C, K на D и A на E, чтобы обеспечить правильный порядок ASCII.
Полный исходный код для перетасовки и раздачи карт https://dedetoknotes.blogspot.com/2024/07/java-17-shuffle-52-playing-cards-and.html
код
...
//int posCard = beforeOrAfter(card2Sort, cardIterate, 0, iterMax);
int posCard = beforeOrAfter2(card2Sort, cardIterate);
...
/* -1 wordA before wordB
* 0 wordA equal wordB
* 1 wordA after wordB
* suite order less C D H S higher
* value suit less 0 1 2 3 4 5 6 7 8 9 A J K Q
* map 10 to A
* J to B
* Q to C
* K to D
* A to E
*/
private static int beforeOrAfter2(String wordA, String wordB) {
//System.out.print("word "+wordA+" "+wordB+" "); // debug
char charA = wordA.charAt(wordA.length()-1);
char charB = wordB.charAt(wordB.length()-1);
//System.out.println(wordA+" "+wordB+" End "+charA+" "+charB+" "); // debug
if (charA<charB) {
return -1;
} else if (charA>charB) {
return 1;
} else {
// equal
charA = wordA.charAt(wordA.length()-2);
charB = wordB.charAt(wordB.length()-2);
// mapping charA to correct order
if (charA=='0') {
charA='A';
} else if (charA=='J') {
charA='B';
} else if (charA=='Q') {
charA='C';
} else if (charA=='K') {
charA='D';
} else if (charA=='A') {
charA='E';
}
// mapping charB to correct order
if (charB=='0') {
charB='A';
} else if (charB=='J') {
charB='B';
} else if (charB=='Q') {
charB='C';
} else if (charB=='K') {
charB='D';
} else if (charB=='A') {
charB='E';
}
//System.out.println(" First "+charA+" "+charB); // debug
if (charA<charB) {
return -1;
} else if (charA>charB) {
return 1;
}
}
return 0;
}
Ключевое слово — "mapping"
. Так что пропустите конструкции if/else
и используйте одну. Map<Character,Integer> map = Map.of('0', 1, 'J', 2, 'Q', 3, 'A', 4); return map.get(charA).compareTo(map.get(charB));
Обратите внимание, что вы можете определить карту вне метода.
Обычно для раздачи карт лучше использовать рандомайзер. Затем вы можете выбрать как костюм, так и номинал на основе возвращенного числа из случайного значения в диапазоне [0..51].