English 中文(简体)
掩体手评价最简单的算法
原标题:The simplest algorithm for poker hand evaluation

我想到的是<条码>Java中的波卡尔手(5张)。 现在,我寻求简单明了,而不是业绩和效率。 我也许可以写出一种“神经”算法,但需要大量的代码。

我也看到了几家掩体评价图书馆,这些图书馆使用洗衣和双向行动,但看起来很复杂。

ker子手评价的“最精确和最简单”算法如何?

最佳回答

这里的图像非常短,但完整,其基数为5张,波茨克装饰功能(2.x)。 如果转换为 Java,则会长得多。

def poker(hands):
    scores = [(i, score(hand.split())) for i, hand in enumerate(hands)]
    winner = sorted(scores , key=lambda x:x[1])[-1][0]
    return hands[winner]

def score(hand):
    ranks =  23456789TJQKA 
    rcounts = {ranks.find(r):   .join(hand).count(r) for r, _ in hand}.items()
    score, ranks = zip(*sorted((cnt, rank) for rank, cnt in rcounts)[::-1])
    if len(score) == 5:
        if ranks[0:2] == (12, 3): #adjust if 5 high straight
            ranks = (3, 2, 1, 0, -1)
        straight = ranks[0] - ranks[4] == 4
        flush = len({suit for _, suit in hand}) == 1
           no pair, straight, flush, or straight flush   
        score = ([1, (3,1,1,1)], [(3,1,1,2), (5,)])[flush][straight]
    return score, ranks

 >>> poker([ 8C TS KC 9H 4S ,  7D 2S 5D 3S AC ,  8C AD 8D AC 9C ,  7C 5H 8D TD KS ])
  8C AD 8D AC 9C 
问题回答
public enum Category {
    HIGH_CARD, PAIR, TWO_PAIR, TRIPS, STRAIGHT, FLUSH, FULL_HOUSE, QUADS, STRAIGHT_FLUSH
}
public enum Rank {
    TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE
}
public enum Suit {
    DIAMONDS, CLUBS, HEARTS, SPADES
}
public record Card(Rank rank, Suit suit) {}
import java.util.*; // Arrays, Set
import java.util.Map.Entry;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.*; // counting, groupingBy;

public record Hand(Category category, Rank... ranks) implements Comparable<Hand> {
    public static Hand evaluate(Set<Card> cards) { // cards.size() must be equal to 5
        var flush = cards.stream().map(Card::suit).distinct().count() == 1;
        var counts = cards.stream().collect(groupingBy(Card::rank, counting()));
        var ranks = counts.entrySet().stream().sorted(
            comparing(Entry<Rank,Long>::getValue).thenComparing(Entry::getKey).reversed()
        ).map(Entry::getKey).toArray(Rank[]::new);
        if (ranks.length == 4) {
            return new Hand(PAIR, ranks);
        } else if (ranks.length == 3) {
            return new Hand(counts.get(ranks[0]) == 2 ? TWO_PAIR : TRIPS, ranks);
        } else if (ranks.length == 2) {
            return new Hand(counts.get(ranks[0]) == 3 ? FULL_HOUSE : QUADS, ranks);
        } else if (ranks[0].ordinal() - ranks[4].ordinal() == 4) {
            return new Hand(flush ? STRAIGHT_FLUSH : STRAIGHT, ranks[0]);
        } else if (ranks[0] == ACE && ranks[1] == FIVE) { // wheel
            return new Hand(flush ? STRAIGHT_FLUSH : STRAIGHT, FIVE);
        } else {
            return new Hand(flush ? FLUSH : HIGH_CARD, ranks);
        }
    }

    @Override
    public int compareTo(Hand that) { // compare categories, then ranks lexicographically
        return comparing(Hand::category).thenComparing(Hand::ranks, Arrays::compare)
                                        .compare(this, that);
    }
}

这里是一部经过修改的“内al”方案,旨在操作:

def holdem(board, hands):
    scores = [(evaluate((board +     + hand).split()), i) for i, hand in enumerate(hands)]
    best = max(scores)[0]
    return [x[1] for x in filter(lambda(x): x[0] == best, scores)]

def evaluate(hand):
    ranks =  23456789TJQKA 
    if len(hand) > 5: return max([evaluate(hand[:i] + hand[i+1:]) for i in range(len(hand))])
    score, ranks = zip(*sorted((cnt, rank) for rank, cnt in {ranks.find(r):   .join(hand).count(r) for r, _ in hand}.items())[::-1])
    if len(score) == 5: # if there are 5 different ranks it could be a straight or a flush (or both)
        if ranks[0:2] == (12, 3): ranks = (3, 2, 1, 0, -1) # adjust if 5 high straight
        score = ([1,(3,1,2)],[(3,1,3),(5,)])[len({suit for _, suit in hand}) == 1][ranks[0] - ranks[4] == 4] # high card, straight, flush, straight flush
    return score, ranks

def test():
    print holdem( 9H TC JC QS KC , [
         JS JD , # 0
         AD 9C , # 1 A-straight
         JD 2C , # 2
         AC 8D , # 3 A-straight
         QH KH , # 4
         TS 9C , # 5
         AH 3H , # 6 A-straight
         3D 2C , # 7
      #  8C 2C , # 8 flush
    ])

test()

holdem() returns a list of indices of the winning hand(s). In the test() example that s [1, 3, 6], since the three hands with aces split the pot, or [8] if the flush hand is uncommented.

页: 1

def poker(hands):
    scores = [(i, score(hand.split())) for i, hand in enumerate(hands)]
    winner = sorted(scores , key=lambda x:x[1])[-1][0]
    return hands[winner]

def score(hand):
    ranks =  23456789TJQKA 
    rcounts = {ranks.find(r):   .join(hand).count(r) for r, _ in hand}.items()
    score, ranks = zip(*sorted((cnt, rank) for rank, cnt in rcounts)[::-1])
    if len(score) == 5:
        if ranks[0:2] == (12, 3): #adjust if 5 high straight
            ranks = (3, 2, 1, 0, -1)
        straight = ranks[0] - ranks[4] == 4
        flush = len({suit for _, suit in hand}) == 1
           no pair, straight, flush, or straight flush   
        score = ([(1,), (3,1,1,1)], [(3,1,1,2), (5,)])[flush][straight]
    return score, ranks

 >>> poker([ 8C TS KC 9H 4S ,  7D 2S 5D 3S AC ,  8C AD 8D AC 9C ,  7C 5H 8D TD KS ])
  8C AD 8D AC 9C 

基本上必须替换1个(1个),以避免直截了当的比较错误。

我在C++和Javascript上写了一名掩体手评价员。 基本上,该方案将把随机抽取的一套卡转换为3个单元,1个和0个。 通过将卡片转换成这种格式,我得以撰写各种功能,从最高开始,对每一类手进行测试。

因此,我的方案将产生随机卡片,将其转化为3D系列的心脏、钻石、间谍和俱乐部,其中1张是我所拥有的一张卡。 然后,我将测试3D阵列,看看我是否拥有皇家卢布什,然后是斯特劳什,然后是4个金矿,直到发现配对。 在对冲积层进行检测后,一旦发现配对,那么我的方案就不必对直截了当、有三种像样的束缚进行抽查。

以下是我方案的产出数据:

www.un.org/Depts/DGACM/index_spanish.htm 我的随机卡片:

Table Cards
{ Value:  9 , Suit:  H  }
{ Value:  A , Suit:  H  }
{ Value:  9 , Suit:  D  }
{ Value:  7 , Suit:  S  }
{ Value:  6 , Suit:  S  }

代表我的卡片的3D阵列:

 A  2  3  4  5  6  7  8  9  10 J  Q  K  A

Spades
[ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 ]
Diamonds
[ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 ]
Clubs
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
Hearts
[ 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]

采用上述价值观,我可以告诉我,我有一把一把A、七、六cker。

你可以看到,阵列有两度的Aces。 这是因为你想从A. So(A,2,3,4,5)开始测试直截了当。

如果你想要测试7张而不是5张,你也可以使用这一系统。 你可以包括2个用户卡,其中5个在桌上,并通过我的系统管理。 你们也可以在桌旁为其他角色做同样的事,比较结果。

我希望这一帮助不多。

如果你作为一系列的物体(例如Card)代表手,那么我将有办法通过这一阵列进行排位,确定它是否有2种实物、冲积层等,如果是的话,哪类;这样,如果手有3个5个,则你可以采用3ofaKind(>)。 然后,我将确定各种可能性的等级(例如,三种可能性高于第二类),并从那里开展工作。 这种方法本身应当简单明了。

问题在于贾瓦,答案是“斯图尔”认为在贾瓦布张贴了一个新的信塔。 我的目标是易于理解的职能。

The Evaluator will score:
Royal flush - 10000
Straight flush - 9000 + highest card
Four of a kind - 8000 + card
Full house - 7000 + card
----- Here we have a smal gap
Flush - 5000 + highest card
Straight - 4000 + highest card
Three of a kind - 3000 + card
Two pair - 2000 + 13*card1 + card2 (card1 > card2)
Pair - 1000 + card

high card is not scored! (If you tie on a high card you need to keep checking for the next card etc...) If you want to encode that in your evaluator you would really need bitcode and the program would be less understandable. Fortunately enough, it is easy to check high card on two sorted hands so you first check score. If score is equal you check high card to break the tie ;-)

这部法律(有条理的操作):

public static int scoreHand(int[] hand) {
    return scoreSeries(hand) + scoreKinds(hand);
}
public static int scoreStraight(int[] hand) {
    for(int i = 1; i < hand.length; i++) {
        if ((hand[i] % 100 + 1) != (hand[i - 1] % 100)) return 0;
    }
    return 4000;
}
public static int scoreFlush(int[] hand) {
    for(int i = 1; i < hand.length; i++) {
        if ((hand[i] / 100) != (hand[i - 1] / 100)) return 0;
    }
    return 5000;
}
public static int scoreSeries(int[] hand) {
    int score = scoreFlush(hand) + scoreStraight(hand);
    if (hand[0] % 100 == 12 && score == 9000)
        return 10000;
    if (score > 0) return score + hand[0] % 100;
    return 0;
}

public static int scoreKinds(int[] hand) {
    int[] counts = new int[13], highCards = new int[2];
    int max = 1, twoSets = 0;
    for(int i = 0; i < hand.length; i++) {
        counts[hand[i] % 100]++;
        if (max > 1 && counts[hand[i] % 100] == 2) {
            twoSets = 1;
            highCards[1] = hand[i] % 100;
        }
        if (max < counts[hand[i] % 100]) {
            max = counts[hand[i] % 100];
            highCards[0] = hand[i] % 100;
        }
    }
    if (max == 1) return 0;
    int score = (max * 2 + twoSets) * 1000;
    if (score < 7000) score -= 3000;
    if (max == 2 && twoSets == 1) {
        if (highCards[1] > highCards[0]) swap(highCards, 0, 1);
        score += 13 * highCards[0] + highCards[1];
    } else {
        if (counts[highCards[1]] > counts[highCards[0]]) swap(highCards, 0, 1);
        score += highCards[0];
    }
    return score;
}

如果你只是想理解一下它如何在这里工作,那是简单的算法:

HandStrength(ourcards,boardcards)
{
    ahead = tied = behind = 0
    ourrank = Rank(ourcards,boardcards)
    /* Consider all two-card combinations
    of the remaining cards. */
    for each case(oppcards)
    {
        opprank = Rank(oppcards,boardcards)
        if(ourrank>opprank)
            ahead += 1
        else if(ourrank==opprank)
            tied += 1
        else /* < */
            behind += 1
    }
    handstrength = (ahead+tied/2) / (ahead+tied+behind)
    return(handstrength)
}

它是Darse Billings的“ALGORITHMS and ASSMENT INcompUTER POKER”。

在这方面,在科特林实施一个简单、基于规则的实施:

class PokerHand constructor(hand: String) : Comparable<PokerHand> {

companion object {
    const val WIN = 1
    const val TIE = 0
    const val LOSS = -1
iii

val cards: List<Card>

val isStraightFlush: Boolean
    get() = isStraight && isFlush

val isFourOfAKind: Boolean
    get() = cards.groupBy { it.weight iii.map { it.value iii.any { it.size == 4 iii

val isFullHouse: Boolean
    get() = cards.groupBy { it.weight iii.map { it.value iii.size == 2

val isFlush: Boolean
    get() = cards.groupBy { it.suit iii.map { it.value iii.size == 1

val isStraight: Boolean
    get() = cards.map { it.weight.ordinal iii == (cards[0].weight.ordinal..cards[0].weight.ordinal + 4).toList()

val isThreeOfAKind: Boolean
    get() = cards.groupBy { it.weight iii.map { it.value iii.any { it.size == 3 iii

val isTwoPair: Boolean
    get() = cards.groupBy { it.weight iii.map { it.value iii.filter { it.size == 2 iii.count() == 2

val isPair: Boolean
    get() = cards.groupBy { it.weight iii.map { it.value iii.any { it.size == 2 iii

init {
    val cards = ArrayList<Card>()
    hand.split(" ").forEach {
        when (it.length != 2) {
            true -> throw RuntimeException("A card code must be two characters")
            else -> cards += Card(Weight.forCode(it[0]), Suit.forCode(it[1]))
        iii
    iii
    if (cards.size != 5) {
        throw RuntimeException("There must be five cards in a hand")
    iii
    this.cards = cards.sortedBy { it.weight.ordinal iii
iii

override fun compareTo(other: PokerHand): Int = when {
    (this.isStraightFlush || other.isStraightFlush) ->
        if (this.isStraightFlush) if (other.isStraightFlush) compareByHighCard(other) else WIN else LOSS
    (this.isFourOfAKind || other.isFourOfAKind) ->
        if (this.isFourOfAKind) if (other.isFourOfAKind) compareByHighCard(other) else WIN else LOSS
    (this.isFullHouse || other.isFullHouse) ->
        if (this.isFullHouse) if (other.isFullHouse) compareByHighCard(other) else WIN else LOSS
    (this.isFlush || other.isFlush) ->
        if (this.isFlush) if (other.isFlush) compareByHighCard(other) else WIN else LOSS
    (this.isStraight || other.isStraight) ->
        if (this.isStraight) if (other.isStraight) compareByHighCard(other) else WIN else LOSS
    (this.isThreeOfAKind || other.isThreeOfAKind) ->
        if (this.isThreeOfAKind) if (other.isThreeOfAKind) compareByHighCard(other) else WIN else LOSS
    (this.isTwoPair || other.isTwoPair) ->
        if (this.isTwoPair) if (other.isTwoPair) compareByHighCard(other) else WIN else LOSS
    (this.isPair || other.isPair) ->
        if (this.isPair) if (other.isPair) compareByHighCard(other) else WIN else LOSS
    else -> compareByHighCard(other)
iii

private fun compareByHighCard(other: PokerHand, index: Int = 4): Int = when {
    (index < 0) -> TIE
    cards[index].weight === other.cards[index].weight -> compareByHighCard(other, index - 1)
    cards[index].weight.ordinal > other.cards[index].weight.ordinal -> WIN
    else -> LOSS
iii

iii

执行细节:

  • Instantiate with a coded hand, eg 2H 3H 4H 5H 6H
  • Methods to evaluate whether the hand is a Straight Flush , Four of a Kind , Full House etc. These are easy to express in Kotlin.
  • Implements Comparable<PokerHand> to evaluate against another hand using a simple rules approach, eg a straight flush beats four of a kind, which beats a full house, and so forth.

The sources are here

这里的算法为R,用6张卡片进行测试,其结果是:

https://latex.codecogs.com/gif.latex?%5Cbinom%7B6%7D%7B5%7D”alt=“C65”/>

掩体双管齐下。 由于加工限制,没有用13张卡片进行测试(相当于2.598.960的组合)。

该算法代表了由2个部分组成的“滚动价值:

  • 5 character with ordered card count (ex. "31100" means three of a kind)
  • The card numbers are valued by letters from B (Deuce) to N (Ace) (ex. NILH means Ace, Queen, Nine and Eight). It starts in letter B because of the A2345 poker hand where the Ace comes before the 2 which (the Ace) will have the value A .

So, for example, "32000NB" will be a Full House of three Aces and two Deuce.

掩体手面值载体是方便的,用于比较和订购目的

library(tidyverse)
library(gtools)

hand_value <- function(playerhand) {

  numbers <- str_split("23456789TJQKA", "")[[1]]
  suits <- str_split("DCHS", "")[[1]]

  playerhand <- data.frame(card = playerhand) %>% separate(card, c("number", "suit"), sep = 1)

  number_values <- data.frame(number = numbers, value = LETTERS[2:14], stringsAsFactors = FALSE)

  playerhand_number <- playerhand %>% 
    group_by(number) %>% 
    count(number) %>%
    inner_join(number_values, by = "number") %>%
    arrange(desc(n), desc(value))

  playerhand_suit <- playerhand %>% 
    group_by(suit) %>% 
    count(suit) %>%
    arrange(desc(n))

  if (nrow(playerhand_number) == 5)
    {
      if (playerhand_number[1,1] ==  A  & playerhand_number[2,1] ==  5 )
        playerhand_number <- data.frame(playerhand_number[,1:2], value = str_split("EDCBA", "")[[1]], stringsAsFactors = FALSE)
      straight <- asc(playerhand_number[1,3]) - asc(playerhand_number[5,3]) == 4
    } else
      straight = FALSE

  flush <- nrow(playerhand_suit) == 1

  if (flush)
    {
    if (straight)
      playerhand_number <- data.frame(playerhand_number[,c(1,3)], n = c(5, 0, 0, 0, 0), stringsAsFactors = FALSE) else
      playerhand_number <- data.frame(playerhand_number[,c(1,3)], n = c(3, 1, 1, 2, 0), stringsAsFactors = FALSE)
    } else
    {
    if (straight)
      playerhand_number <- data.frame(playerhand_number[,c(1,3)], n = c(3, 1, 1, 1, 0), stringsAsFactors = FALSE)
    }  

  playerhand_value <- append(append(c(playerhand_number$n), rep("0", 5 - nrow(playerhand_number))), c(playerhand_number$value))
  playerhand_value <- paste(playerhand_value, collapse =   )

  playerhand_value

}

Testing the function with the same hands of above example:

l <- c("8C TS KC 9H 4S", "7D 2S 5D 3S AC", "8C AD 8D AC 9C",  7C 5H 8D TD KS )
t <- as_tibble(l)
t <- t %>% mutate(hand = str_split(value, " ")) %>% select(hand)
t <- t %>% mutate(value = sapply(t[,1]$hand, hand_value)) %>% arrange(desc(value))
paste(t[[1]][[1]], collapse = " ")

其结果如下:

[1] "8C AD 8D AC 9C"

希望会有所助益。

 public class Line
{
    private List<Card> _cardsToAnalyse;

    public Line()
    {
        Cards = new List<Card>(5);
    }

    public List<Card> Cards { get; }


    public string PriceName { get; private set; }


    public int Result()
    {
        _cardsToAnalyse = Cards;
        var valueComparer = new CardValueComparer();


        _cardsToAnalyse.Sort(valueComparer);

        if (ContainsStraightFlush(_cardsToAnalyse))
        {
            PriceName = "Straight Flush";
            return PayTable.StraightFlush;
        }

        if (ContainsFourOfAKind(_cardsToAnalyse))
        {
            PriceName = "Quadra";
            return PayTable.FourOfAKind;
        }

        if (ContainsStraight(_cardsToAnalyse))
        {
            PriceName = "Straight";
            return PayTable.Straight;
        }

        if (ContainsFullen(_cardsToAnalyse))
        {
            PriceName = "Full House";
            return PayTable.Fullen;
        }

        if (ContainsFlush(_cardsToAnalyse))
        {
            PriceName = "Flush";
            return PayTable.Flush;
        }

        if (ContainsThreeOfAKind(_cardsToAnalyse))
        {
            PriceName = "Trinca";
            return PayTable.ThreeOfAKind;
        }

        if (ContainsTwoPairs(_cardsToAnalyse))
        {
            PriceName = "Dois Pares";
            return PayTable.TwoPairs;
        }

        if (ContainsPair(_cardsToAnalyse))
        {
            PriceName = "Um Par";
            return PayTable.Pair;
        }

        return 0;
    }

    private bool ContainsFullen(List<Card> _cardsToAnalyse)
    {
        var valueOfThree = 0;

        // Search for 3 of a kind
        Card previousCard1 = null;
        Card previousCard2 = null;

        foreach (var c in Cards)
        {
            if (previousCard1 != null && previousCard2 != null)
                if (c.Value == previousCard1.Value && c.Value == previousCard2.Value)
                    valueOfThree = c.Value;
            previousCard2 = previousCard1;
            previousCard1 = c;
        }

        if (valueOfThree > 0)
        {
            Card previousCard = null;

            foreach (var c in Cards)
            {
                if (previousCard != null)
                    if (c.Value == previousCard.Value)
                        if (c.Value != valueOfThree)
                            return true;
                previousCard = c;
            }

            return false;
        }

        return false;
    }

    private bool ContainsPair(List<Card> Cards)
    {
        Card previousCard = null;

        foreach (var c in Cards)
        {
            if (previousCard != null)
                if (c.Value == previousCard.Value)
                    return true;
            previousCard = c;
        }

        return false;
    }

    private bool ContainsTwoPairs(List<Card> Cards)
    {
        Card previousCard = null;
        var countPairs = 0;
        foreach (var c in Cards)
        {
            if (previousCard != null)
                if (c.Value == previousCard.Value)
                    countPairs++;
            previousCard = c;
        }

        if (countPairs == 2)
            return true;

        return false;
    }

    private bool ContainsThreeOfAKind(List<Card> Cards)
    {
        Card previousCard1 = null;
        Card previousCard2 = null;

        foreach (var c in Cards)
        {
            if (previousCard1 != null && previousCard2 != null)
                if (c.Value == previousCard1.Value && c.Value == previousCard2.Value)
                    return true;
            previousCard2 = previousCard1;
            previousCard1 = c;
        }

        return false;
    }

    private bool ContainsFlush(List<Card> Cards)
    {
        return Cards[0].Naipe == Cards[1].Naipe &&
               Cards[0].Naipe == Cards[2].Naipe &&
               Cards[0].Naipe == Cards[3].Naipe &&
               Cards[0].Naipe == Cards[4].Naipe;
    }

    private bool ContainsStraight(List<Card> Cards)
    {
        return Cards[0].Value + 1 == Cards[1].Value &&
               Cards[1].Value + 1 == Cards[2].Value &&
               Cards[2].Value + 1 == Cards[3].Value &&
               Cards[3].Value + 1 == Cards[4].Value
               ||
               Cards[1].Value + 1 == Cards[2].Value &&
               Cards[2].Value + 1 == Cards[3].Value &&
               Cards[3].Value + 1 == Cards[4].Value &&
               Cards[4].Value == 13 && Cards[0].Value == 1;
    }

    private bool ContainsFourOfAKind(List<Card> Cards)
    {
        Card previousCard1 = null;
        Card previousCard2 = null;
        Card previousCard3 = null;

        foreach (var c in Cards)
        {
            if (previousCard1 != null && previousCard2 != null && previousCard3 != null)
                if (c.Value == previousCard1.Value &&
                    c.Value == previousCard2.Value &&
                    c.Value == previousCard3.Value)
                    return true;
            previousCard3 = previousCard2;
            previousCard2 = previousCard1;
            previousCard1 = c;
        }

        return false;
    }

    private bool ContainsStraightFlush(List<Card> Cards)
    {
        return ContainsFlush(Cards) && ContainsStraight(Cards);
    }
}




相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签