What is optimal algorithm of "random card distribution (Poker game)"? - algorithm

What is the optimal algorithm of "random card distribution (Poker game)"?
I'd like to build a program that can distribute playing cards evenly with respect to their numbers and suits to the 4 players in a Poker game. In other words, all 4 players should have a straight or straight flush at the same turn. But, how do we build the algorithm for that?
This is how I think it can be approached:
Set i = suits (e.g., 1 for heart 2 for spades) j = number (ace = 1, king =13)
Make a matrix deck (Like [H1, H2, ... H13]...[S1, S2, ... S13])
Randomly choose the first card (random i1 and j1), save to player 1's deck
card = deck[i1, j1]
player1 = np.append(card)
Randomly choose second card (random i2 and j2)
If i2 = i1, rechoose i, if not, save the card to player 2 deck
Continue until 4th card
For 5th turn, if i5 = i1 and j5 = j1+1 or j1-1, save the card to player 1 deck. if not rechoose j or i.
Continue until 8th card
For the 9th turn, if i9 = i1 and j9 = j1+1 or j1-1 or j5+1 or j5-1, save the card to player 1 deck. if no rechoose j or i
However, this algorithm is time-consuming, because I need to introduce a new way to check every 4 turns whether it has a close number of previous cards that the player already has.
Is there any way to make this simpler, such as to check the card having a close number of previous cards for making a straight flush?

The way to approach is the same as if you wanted to deal cards in real life. Here is an example in python
Build a deck of cards:
import itertools
deck = ["".join(c) for c in itertools.product("AKQJT98765432", "cdhs")]
# ['Ac', 'Ad', 'Ah', 'As', 'Kc', 'Kd', ....
Shuffle the deck
import random
random.shuffle(deck)
# ['3c', '5c', '7h', 'Qs', 'Ts', '8s', 'Jd', ...
Deal cards to 4 players
players = []
for i in range(4):
cards = [deck.pop(), deck.pop()]
players.append(cards)
# [['Td', 'Jd'], ['7c', '5c'], ['Js', 'Ks'], ['6c', '8d']]

Related

What's the probability of drawing four different suits when picking five cards?

Considering an infinite and perfectly random deck of cards (or just repositioning the cards and shuffling a normal deck after every drawn card), how do I calculate the probability that exactly x suits will be pulled out with y tries? For example if we draw five cards from the deck, what's the probability of all four suits being present?
I bruteforced this scenario for 5 cards by generating every possible combination and considering it equally likely (since I'm repositioning the cards after every draw), and found the following values: 4/1024 for exactly one suit, 180/1024 for two, 600/1024 for three and 240/1024 for four suits.
The way I bruteforced it was: I generated all possible combinations with five loops iterating from 0 to 4, appending every iteration to a list, then running the following function to check if a combination has exactly n different suits for every element in the list:
def valid(l, n):
arr = [l[0]]
for i in range(0, len(l)):
aux = 0
for j in range(0, len(arr)):
if arr[j] == l[i]:
aux = 1
if aux == 0:
arr.append(l[i])
return len(arr) == n
It's a trivial matter for one suit, however when considering four of them I could not reach the expected value: 1/4^4 for a specific combination (considering the fifth card might be anything), times the number of ways to arrange those 4 cards in 5 slots (120) = 120/256, roughly two times my bruteforced result.
What am I missing here?

Selecting pairs of teams to play each round, with maximum exposure before repeats

I'm writing a sports schedule generator. Given T teams (an even number), G games per round (a multiple of T/2), and R rounds I want to generate a schedule matching the criteria:
All teams play the same number of games in the round.
Team combinations are fully exhausted before they are repeated.
I have an algorithm that works most of the time, but not always. It is detailed at the end of this question. How can I fix (or replace) this algorithm to robustly work for all reasonable inputs?
This question is similar to Sorting pairs of teams with non-repeating | Round-robin tournament and Algorithm: Selecting pairs of teams from a set of games but has distinct requirements.
For example, assume there are T=4 teams. This gives us 6 possible games:
(T0,T1) (T0,T2) (T0,T3) (T1,T2) (T1,T3) (T2,T3)
If there are G=4 games per round, then the first round must not be this set of games…
(T0,T1) (T0,T2) (T0,T3) (T1,T2)
…because T0 gets to play 3 times, but T3 only gets to play once (a violation of requirement #1). Instead, the first round might look like this, where every team gets to play two games:
(T0,T1) (T2,T3) (T0,T2) (T1,T3)
If that same set of games were repeated for the second round, then the two games (T1,T2) and (T0,T3) would never take place (a violation of requirement #2). So, we want to ensure that they are included in the second round before we pick up new games. A valid schedule for T=4, G=4, R=5 would be:
(T0,T1) (T2,T3) (T0,T2) (T1,T3)
(T0,T3) (T1,T2) (T0,T1) (T2,T3)
(T0,T2) (T1,T3) (T0,T3) (T1,T2)
(T0,T1) (T2,T3) (T0,T2) (T1,T3)
(T0,T3) (T1,T2) (T0,T1) (T2,T3)
As seen, for large values of R it is acceptable for the set of games in a round to be repeated eventually.
The algorithm I have works like so:
Calculate all unique pairwise combinations of teams (the possible games). Save this list as currentPool.
Create an empty list, named otherPool.
For each round, G times perform the following:
Find the game in currentPool with the lowest sum when adding together the number of times each team in the game has been seen in this round.
Add the game to the round.
Move the game from the currentPool to the otherPool.
If currentPool is empty, swap currentPool and otherPool.
For many reasonable values of T, G, and R this algorithm works. However, there are some combinations that fail. For example, with T=6, G=3, R=5, it generates this schedule:
(T0,T1) (T2,T3) (T4,T5)
(T0,T2) (T1,T3) (T0,T4)
(T0,T3) (T1,T2) (T0,T5)
(T1,T4) (T2,T5) (T3,T4)
(T1,T5) (T2,T4) (T3,T5)
The first round is correct, but in the second round T0 plays twice and T5 never gets to play. The problem is easy to spot—after picking (T0,T2) and (T1,T3) in round 2 the only game that is possible to satisfy requirement #1 would be (T4,T5), but that game was already used in the first round and per requirement #2 cannot be re-used until all 15 unique games are used up. The algorithm started down a dead-end and had no way to back track.
Finally, for completeness here is a JavaScript version of the algorithm described. Here's sample output of a successful run:
let schedule = singleFieldSchedule({
teams : 8,
maxGamesPerRound : 12,
rounds : 8
})
console.log(schedule.map(round => round.map(game => `(T${game[0]},T${game[1]})`).join(' ')).join('\n') )
(T0,T1) (T2,T3) (T4,T5) (T6,T7) (T0,T2) (T1,T3) (T4,T6) (T5,T7) (T0,T3) (T1,T2) (T4,T7) (T5,T6)
(T0,T4) (T1,T5) (T2,T6) (T3,T7) (T0,T5) (T1,T4) (T2,T7) (T3,T6) (T0,T6) (T1,T7) (T2,T4) (T3,T5)
(T0,T7) (T1,T6) (T2,T5) (T3,T4) (T0,T1) (T2,T3) (T4,T5) (T6,T7) (T0,T2) (T1,T3) (T4,T6) (T5,T7)
(T0,T3) (T1,T2) (T4,T7) (T5,T6) (T0,T4) (T1,T5) (T2,T6) (T3,T7) (T0,T5) (T1,T4) (T2,T7) (T3,T6)
(T0,T6) (T1,T7) (T2,T4) (T3,T5) (T0,T7) (T1,T6) (T2,T5) (T3,T4) (T0,T1) (T2,T3) (T4,T5) (T6,T7)
(T0,T2) (T1,T3) (T4,T6) (T5,T7) (T0,T3) (T1,T2) (T4,T7) (T5,T6) (T0,T4) (T1,T5) (T2,T6) (T3,T7)
(T0,T5) (T1,T4) (T2,T7) (T3,T6) (T0,T6) (T1,T7) (T2,T4) (T3,T5) (T0,T7) (T1,T6) (T2,T5) (T3,T4)
(T0,T1) (T2,T3) (T4,T5) (T6,T7) (T0,T2) (T1,T3) (T4,T6) (T5,T7) (T0,T3) (T1,T2) (T4,T7) (T5,T6)
function singleFieldSchedule({teams=8, maxGamesPerRound=12, rounds=8}={}) {
const uniquePairs = a => a.reduce((res,o1,i,a) => res.concat(a.slice(i+1).map(o2 => [o1,o2])), [])
const teamNames = Array.from(Array(teams).keys())
const fullExposure = uniquePairs(teamNames)
const zeroTeamCounts = teamNames.map(n => [n,0])
// Calculate how many games can be played by each team while keeping things fair
const gamesPerTeam = Math.floor(maxGamesPerRound / teams * 2)
const gamesPerRound = gamesPerTeam * teams/2
const schedule = []
const pools = [fullExposure, []]
let poolIndex = 0
for (let r=0; r<rounds; ++r) {
const round = []
schedule.push(round)
const gamesPerTeam = new Map(zeroTeamCounts)
for (let g=0; g<gamesPerRound; ++g) {
let pool = pools[poolIndex]
if (!pool.length) pool = pools[poolIndex=((poolIndex+1)%2)]
// Find the game whose teams have been seen the least
let bestGameSum = Infinity
let bestGameIndex
for (i=0; i<pool.length; ++i) {
const game = pool[i];
// We square the times seen to favor a game where each team has been seen once
// over a game where one team has been seen twice and the other team has never been seen
const gameSum = gamesPerTeam.get(game[0])**2 + gamesPerTeam.get(game[1])**2
if (gameSum < bestGameSum) {
bestGameSum = gameSum
bestGameIndex = i
}
}
let bestGame = pool.splice(bestGameIndex, 1)[0]
round.push(bestGame)
gamesPerTeam.set(bestGame[0], gamesPerTeam.get(bestGame[0])+1);
gamesPerTeam.set(bestGame[1], gamesPerTeam.get(bestGame[1])+1);
// Put this game into the 'other' pool, to be used once this pool of games is used up
pools[(poolIndex+1) % 2].push(bestGame)
}
// Check to see if any team got screwed this round
const shortedTeams = teamNames.filter(t => gamesPerTeam.get(t)<gamesPerTeam)
shortedTeams.forEach( t => {
const ct = gamesPerTeam.get(t)
console.warn(`Team ${t} only got to play ${ct}/${gamesPerTeam} games in round #${r}`)
})
}
return schedule
}
Lay out a standard round-robin schedule. Then merely take the pairings in order for the quantity of matches you want for each of your rounds.
"The standard schedule" pairs up the teams and rotates all but the first team for each round. For six teams, the schedule looks like this; pairings are adjacent vertically:
0 1 2
5 4 3
0 5 1
4 3 2
0 4 5
3 2 1
0 3 4
2 1 5
0 2 3
1 5 4
There it is: five rounds, each team playing each other team exactly once.
If you have an odd quantity of teams, then designate team 0 as the "bye".
If you need rounds of 6 matches, simply pick them in the order given above, left-to-right in each row:
0-5 1-4 2-3 0-4 5-3 1-2
0-3 4-2 5-1 ... etc.
With 2N teams in the league, the lag between matches is N-1, N, or N+1 matches.
I don't have a theoretically perfect solution, but I have a polynomial time approach that should work better.
The heart of it is the Blossom Algorithm for maximal matchings. In each round, use that with edges representing as yet unplayed games. This will more more likely to find valid solutions of the simple cases that can fail with your current algorithm. In particular you have guaranteed that teams cannot play 2 games in a round, and as many unused games as possible get used.
But we can improve on this by noting that we can use a variation to find maximal weight matchings. If you make the weight of each edge be G^i where G is the number of games played and i is the number of rounds since a particular game was played last, then teams cannot play 2 games in a round, and we play with as many games as old possible.
This algorithm guarantees your first condition, and makes a good faith effort to do well by your second. But it does not guarantee the second. (However if you do have early repeats, they will be pretty well spread out.)
If you have a large number of rounds, you can play around with the weight condition to make sure that each one is used on average the right number of times.
In graph terms, the set of all games possible by all teams is a "complete graph", that is, a graph with one vertex per team with edges connecting every pair of teams.
Complete graphs for T=6 and T=8
Finding pairs of games where all teams play exactly once is finding a "perfect matching" of the graph: finding edges that touch every vertex, with no vertex being touched by more than one edge.
Example perfect matches for T=6 and T=8
Ensuring that all possible games are played—finding a set of "perfect matches" that uniquely select every edge—is a 1-factorization of the graph. Following are two different 1-factorizations of the T=6 and T=8 cases. The first of each was hand-created, while the second uses the round-robin algorithm described in the accepted answer.
Given the ability to generate any single 1-factorization for the graph, the problem is solved as following:
Create a 1-factorization of the complete graph representing the number of teams.
Calculate the number of times a single team plays per round N as 2*G/T.
For each round, select N perfect matches from the 1-factorization and use those edges as the games to play that round.
On subsequent rounds, use subsequent perfect matches, such that those not used in the first round will be used later.
Once all perfect matches have been used up, repeat the selection of perfect matches.
There is no requirement to calculate all 1-factorizations. Doing so would provide variety not experienced by players on the teams. For example, above the two 1-factorizations for T=6 show different perfect matches in the case where team A plays team F. However, while teams A and F are playing each other they are likely unaffected by whether team B is playing team D or team C.
A JavaScript version of this algorithm follows:
// Calculate a round-robin schedule using the 'circle' algorithm
// https://en.wikipedia.org/wiki/Round-robin_tournament#Scheduling_algorithm
function roundRobin(teams) {
const loop = Array.from(Array(teams).keys())
const rounds = []
for (let i=0; i<(teams-1); ++i) {
const round = []
for (let j=0; j<teams/2; ++j) {
round.push([loop[j], loop[teams-j-1]].sort())
}
loop.splice(1, 0, loop.pop()) // rotate the 'table'
rounds.push(round)
}
return rounds
}
// Play multiple rounds of a round-robin tournament per a single 'round',
// while ensuring that every team plays the same number of games each round,
// and that every team plays every other team as soon as possible.
function multiGameRobin({teams=8, maxGamesPerRound=12, rounds=8}={}) {
if (teams%2) console.error('number of teams must be even')
const subrounds = roundRobin(teams)
const gamesPerTeam = Math.floor(maxGamesPerRound / teams * 2)
const schedule = []
for (let r=0; r<rounds; ++r) {
let round = []
for (let i=0; i<gamesPerTeam; ++i) {
round = round.concat(subrounds[(r*gamesPerTeam+i) % subrounds.length])
}
schedule[r] = round
}
return schedule
}
What may be interesting—though not a requirement from the original question—is to provide different combinations of perfect matches in subsequent rounds. For example, for T=6 there are 5 different perfect matches, which we might call PM1, PM2, PM3, PM4, and PM5. If in round 1 we use PM1, PM2, and PM3, in round 6 we might use PM1, PM3, and PM5 instead to provide even more variety, so that it is not a direct repeat of the games in round 1.

Count number of ways player 1 can win in a 2 player game

I came across this following question,
2 players play a game. In each turn both players receive points in the range -x to +x (both included). Player 1 starts from score p1 and player 2 starts from score p2. If they play a total of k turns, find the total number of ways in which player 1 can win the game i.e at the end of k turns, player 1 has more points than player 2.
So in short my understanding is that we need to find total number of combination set of points for player1 and player2 such that (sum of points set of player 1)-(sum of points set of player 2) >= p2-p1+1
I am not sure how to tackle this problem. Please suggest an approach. Thanks in advance.
Solve this recursively. Using your variables, let's look at the cases:. Let
score_range = [-x : x]
Call the function win_count
Base case, k==1
if k == 1, there is one turn to go. The score difference is p2-p1. If player 2 scores n2 points in this round, then player 1 needs to finish with at least (p2 + n2) - p1 + 1 points this round. Now, how many combinations of that are available with p1, p2 in score_range ? You can compute that directly from the given integers.
Return that count as the functional value.
Recursion case, k > 1
Walk through all of the possible scores for this round. Recur to count possibilities for the remainder of the game.
count = 0
for n1 in score_range
for n2 in score_range
count += win_count(p1+n1, p2+n2, k-1, x)
Can you take it from there?

Algorithm: Maximizing profit in card game with m winning cards and n losing cards

Let's say a Casino (C) has a game which involves only one player and one dealer. The game is played with m+n cards, m are marked as winning cards and 'n' as losing cards.
Rules/Information regarding the game :
Player knows the number of winning cards 'm' and number of losing cards 'n' at every stage.
Player starts playing with 'X' amount and plays until all the cards are drawn out.
Dealer is very very smart, and has the power to draw either a winning card or a loosing card based on the bet placed by Player on table.
Every draw reduces the number of cards of either category, i.e. if winning card is drawn, number of winning cards becomes 'm-1' and vice versa.
Player can bet '0' amount as well.
If player bets 'W' amount and a winning card is drawn. Player gets 2W in return else he loses the betted amount
Question : Derive an algorithm or strategy which player should follow to maximize his profit.
Some examples :
Testcase - 1:
Lets say m=0, n=1
Player knows dealer has no chance but to make him lose on whatever he bets, so he bets '0' amount. Thus, maximum he can make is X.
Testcase - 2:
m=1, n=0
Player knows dealer has no option but to draw the only card i.e. winning card so he bets everything i.e. 'X' and gets back '2X'. So, he backs out of casino with 2X amount.
TestCase - 3:
m=1, n=1 :
Lets say player bets 'W' amount
- Lets say Dealer draws winning card: so net amount = X+W and m->0 and n->1 : Thus maximum amount in this case X+W
-If dealer draws losing card: so net amount left = X-W and m->1 and n->0 : Thus, maximum amount in this case 2(X-W)
Player will choose 'W' to maximize his profit which can be done only in the case when 2(X-W)=X+W => W=X/3
Thus, maximum amount player can walk out in this case = 4X/3
Here is a solution from F#
Suggestion: don't do symbolic programming unless you have to. In this case we assume X = 1
let stake = Array2D.zeroCreate 100 100
let bankroll = Array2D.zeroCreate 100 100
for i in 1 .. 99 do
stake.[0, i] <- 0.0
bankroll.[0, i] <- 1.0
for i in 1 .. 99 do
stake.[i, 0] <- 1.0
bankroll.[i, 0] <- 2.0
stake.[0, 0] <- 0.0
bankroll.[0, 0] <- 1.0
let rec solve i j =
if bankroll.[i, j] <> 0.0 then (stake.[i, j], bankroll.[i, j])
else
let a = snd (solve (i - 1) j)
let b = snd (solve i (j - 1))
let x = (b - a) / (a + b) // solve (1 + x)a = (1 - x)b
let y = (x + 1.0) * a
stake.[i, j] <- x
bankroll.[i, j] <- y
(x, y)
solve 10 10 // = (0.06182352702, 1.333333333)
It seems as long as the number of winning cards equals the number of losing cards, the max profit a player can gain is always 4X/3

Algorithm: finding the biggest element of a list

The catch: only comparisons between elements of the list is allowed. For example, suppose we have 1,000,000 chess players, and we are assigned the task of finding the best chess player in the group. We can play one chess player against any other chess player. Now, we want to minimize the maximum number of games any player plays.
If player A beats player B, and B beats C, we can assume that A is better than C. What is the smallest n such that no player plays more than n games?
#Carl: This is not homework; it's actually a subproblem of a larger problem from SPOJ.
I would wager a guess that the answer is the binary log of the number of people.
You set up a binary tree as a tournament ladder. This means the most games anyone plays is the height of the tree. The height of the binary tree would be log n
How do I find the biggest element of a list
If the list is ordered, then the biggest element is the first (or last) element of the list.
If the list is not ordered then:
Element biggest = list.get(0);
for (Element e : list) {
if (e.compareWith(biggest) > 0) {
biggest = e;
}
}
For example, suppose we have 1,000,000 chess players, and we are assigned the task of finding the best chess player in the group. Now, we want to minimize the maximum number of games any player plays.
With the new constraint of the last sentence ...
Answer #1: zero games played. Compare the chess player's rankings and the one with the best ranking is the objectively best player ... according to the ranking.
Answer #2: at most ceiling(log2(nos_players)) games played per player. A "knock out" / elimination tournament eliminates half the players in each round, so the number of rounds and hence the maximum number of games played by any one player is ceiling(log2(nos_players)).
The corresponding algorithm is trivially:
List players = ...
while (players.size() > 1) {
List winners = new ArrayList();
Iterator it = players.iterator();
while (it.hasNext()) {
Player p1 = it.next();
if (it.hasNext()) {
Player p2 = it.next();
int result = p1.compareTo(p2);
if (result < 0) {
winners.add(p2);
} else if (result > 0) {
winners.add(p1);
} else {
throw new Exception("draws are impossible in chess");
}
} else {
winners.add(p1); // bye
}
}
players = winners;
}
(Aside: if you also have a predetermined ranking for the players and the number of players N is at least 2 less than ceiling(log2(N)), you can arrange that the best 2 players get a bye in one round. If the best 2 players meet in the final, then everyone will have played less than ceiling(log2(N)) games ... which is an improvement on the solution where the byes are allocated randomly.)
In reality, answer #2 does not work for the game of chess because it does not take account of the fact that a significant percentage of real chess games are draws; i.e. neither player wins. Indeed, the fact that player A beat player B in one game does not mean A is a better player than B. To determine who is the better of any two players they need to play a number of games and tally the wins and losses. In short, the notion that there is a "better than" relation for chess players is TOTALLY unrealistic.
Not withstanding the points above, knock-out is NOT a practical way to organize a chess tournament. Everyone will be camped out on the tournament organizer's desk complaining about having to play games against players much better (or worse) than themselves.
The way a real chess (or similar) tournament works is that you decide on the number of rounds you want to play first. Then in a "round-robin" tournament, you select the top N players by ranking. and arrange that each player plays each other player. The player with the best win / draw score is the winner, and in the event of a tie you use (say) "sum of opponents scores" as a tie breaker. There are other styles of tournament as well that cater for more players / less rounds.
As far as I know there is no algorithm to solve your problem without any additional outside information to rank the players (such as seeding). If you could seed the players appropriately you can find the best player in less rounds than the worst case suggested by J. Wong.
Example of the results of 2 rounds of 10 players: A is the best, ceil(log 10) = 4
A > B; C > D; E > F; G > H; I > J
A > C; B > E; F > G; D > I
Instead of building an Abstract Data Structure such as a binary tree and resolving a tournament, you could re-interpret your goal in a different light:
Eliminate all the elements on the list that are not the largest
You will find that doing this may be much more algorithmically expedient than building a tree and seeding a "tournament".
I can demonstrate that eliminating all elements on a list that are not the largest can be done with a worst-case scenario of log n calls/comparisons per element.
Work on a copy of your original list if possible.
Pair off consecutive elements and remove from the list the lower-valued of the two. Ignore the unpaired element, if there is one.
This can be done by iterating from 0 <= i < int(n/2) and comparing indices 2i and 2i+1.
i.e., for n=7, int(n/2) = 3, i = 0,1,2; compare indices 0 and 1, 2 and 3, 4 and 5.
There should be a total of int(n/2) indices eliminated. Subtract that count from n. Then, repeat 1 until there is only one index remaining. This will be your largest.
Here is an implementation in Ruby:
def find_largest(list)
n = list.size
working_list = list.clone()
while n > 1
temp_list = Array.new()
for i in (0...n/2) # remember to cast n/2 to integer if not automatic
if working_list[2*i] > working_list[2*i+1]
new_list.push(working_list[2*i])
else
new_list.push(working_list[2*i+1])
end
end
working_list = temp_list
n -= n/2 # remember to cast n/2 to integer if not automatic
end
return working_list[0]
end

Resources