Mark Ayzenshtat, Hanhua Feng, and Stephen Lee
November 6, 2003
A few classes before the tournament, it became apparent that many players had matured to the point of exhaustively inferring almost all the information that could be inferred from all player actions during a game. These players had found all absolute truths. Additionally, it seemed as if the ranking difference between players was often simply determined by the move order. Clearly, success in the tournament rankings would be achieved by the players who could make correct guesses before all the other players had enough information to confidently guess.
Our players could gain an advantage over other players if we made some
assumptions about our opponents. Group1Player2 puts its assumption into
use early in the game. Group1Player3 tries to guess early based on the
assumption that a player, will probably not ask another player,
about cards which
already knows
owns.
Before we could implement this kind of decision scheme, we needed to figure out precisely how player actions over the course of a game would be ``remembered'' and how they would specifically affect the probability matrix. In class, we observed that it was possible to represent the constraints on player-card ownership as a set of logic expressions. We decided to maintain such an expression set and with it encapsulate all of the knowledge our player has about other players' card possessions. Then, each round, we would assign probabilities to the matrix based on these clauses, update the matrix as needed, and then formulate decisions based on the resulting probabilities.
To realize this multi-stage decision scheme, we initially made use of the Orbital logic library. Our first player defined a logic term for each player/card combination (e.g. the term representing whether player 3 possessed card 4 would be ). For each action, the player would create a logic expression comprised of such terms. For example, after player 2 makes a guess, all of its cards are revealed. When this occurs, our player would create two expressions: one to signify that player 2 possesses all of the revealed cards:
![]() |
(1) |
![]() |
(2) |
The player would maintain a master list of these expressions as premises. Resolving these premises, it would then assign each player/card term a value of true, false, or unknown. From these discrete assignments, we suspected that it would then be possible to generate the real number probabilities that would populate the final matrix.
We soon realized, however, that it was quite difficult to correctly ``balance'' the matrix. Whenever a single cell value was changed, it required numerous other values to be updated as well in order to preserve row and column sums. Effectively, every cell update potentially necessitated a simultaneous reevaluation of the entire matrix. We ultimately agreed that it would be easier to use a decision table of discrete values (and possibly lose some decision-making capability) than to devote too much time trying to figure out how to efficiently carry out this reevaluation.
This player contains two parts: Counting Logic and the Interrogation Analyzer. The two parts are somewhat independent; the Counting Logic totally ignores the existence of the interrogation strategies, while the Interrogation Analyzer more or less treats the Counting Logic as a black box which provides some state variables.
Although we originally intended to use the Orbital library mentioned above for this purpose, we soon realized that the Counting Logic could be implemented without using the vast number of options provided by Orbital. Complete logic inferences using Orbital consume a lot of memory and are slow to compute. Thus, we implemented our own logic class, CountingLogic.
The Counting Logic class contains a matrix and a list. One dimension of the matrix spans the cards, and the other enumerates the players. The values represent an ownership state, which details whether the card is owned, unowned, or carries an unknown status with respect to the player. Each entry of the matrix has three possible values: positive, negative, and zero. A positive value indicates that the player holds this card. A negative one indicates that the player does not. Lastly and most importantly, a zero value means the relationship between this player and this card is uncertain.
In addition to this matrix, the Counting Logic class maintains two arrays: one indicates how many cards each player holds and the other indicates how many players each card can be held by. The latter array is filled by ones. We made the latter array more general such that a card can be held by more than one player, standard playing cards has four copies of each card, which might make this problem more interesting.
The list contains unresolved clues. A clue contains four elements: a list of cards, a player index, a lower bound and an upper bound of the number indicating how many cards this player is possibly holding from the list of cards. Again, we made this problem more general here.
A clue can be reduced by removing known facts from the list according to the matrix. If the corresponding entry of a removed card in the matrix is positive, the lower bound and the upper bound were decremented by one. After reduction, the clue would be in one of four states:
When a clue is added to the Counting Logic class, we do the following:
Although the processing behind the Counting Logic is simple and efficient,
the list of inferences that can be made is not complete. Therefore, we
add one more reduction method. Crazy Broomhead will arbitrarily
choose a card from the shortest unsolved clues and try to set the
corresponding entries of the matrix to both positive and negative, and
then propagate this change in the matrix to the list of unsolved clues.
After propagation, if some entries are originally zero and repeatedly
resolves to the same non-zero value for every trial, then this entry is
deterministic. Crazy Broomhead will change the original matrix
accordingly. If an ``InvalidClueException'' is caught during trial,
the program will set this entry to the alternate value.
Figure illustrates how this algorithm works.
![]() |
We classified clues into two types, undeniable clues and probable clues. The fol lowing are undeniable clues:
Before interrogation, we decide whom to interrogate. Super Crazy Broomhead chooses the player holding the most number of uncertain cards as its next target of interrogation. However, Crazy Broomhead will arbitrarily designate a player to be unmolested for the purpose of the end-game strategy. Once a target has been selected, Super Crazy Broomhead has two strategies of interrogation: the open-game strategy and the end-game strategy. If there is only one player holding uncertain cards, the program uses end-game strategy. Therefore, in two-player games, the program only employs the end-game strategy.
All cards besides those certainly held by the interrogatee might be added to the padding list. For the open-game strategy, the chance that a card is added to the padding list is three out of four; for the end-game strategy, it is one out of four, since we do not want other players to get more information from our interrogation.
Ideally, the Crazy Broomhead player should hide as much information as possible. Thus, Crazy Broomhead prefers to respond to an interrogation with a card that has been disclosed. Otherwise, if it must disclose a new card, it should respond with a random card. Internally, it maintains two lists of its own cards. One is the disclosed, initially empty list. The other contains the remaining cards, an initially randomized list of its own cards. If Crazy Broomhead becomes the subject of an interrogation, it will first try to respond with a disclosed card. If it cannot find one, it will use the first card in the undisclosed list and move this card to the tail of the disclosed list.
If the rate of success is greater than 95%, the Crazy Broomhead
makes a guess according to the matrix. In other words, if the number
of possible combinations of hidden cards is less than 20 out of 19,
Crazy Broomhead guesses. All cards whose corresponding value are
positive will be added to the guess list. A guess list is made up by the
uncertain cards for the remaining guess slots. For the non-probabilistic
implementation, 95% actually means 100% certain.
The original players Broomhead and Crazy Broomhead use only undeniable
clues. However, we found every player seems to be computing the logic
well. Inevitably, game results would be quite random. Consequently,
we implemented ``Super Crazy Broomhead''. It assumes most of the players
would interrogate other players using a long list and wait for a positive
response in multiple player game. During the first round,
when players are only aware of their own cards, they typically
ask for all unknown cards and pad the list with some of their own cards.
Therefore, we assume that all not present in its first interrogation are
held by the the interrogator. Super Crazy Broomhead utilizes this
probable clue only for the first round; afterward, it resumes its usual
interrogation strategy. Of course, no player can hold
more than cards. If, during the first round, a player interrogates
another player
about a set with fewer than
cards, Super Crazy Broomhead
ignores this interrogation. Thus, Super Crazy Broomhead collects a lot
of information during the first round, much more than most other players do.
Meanwhile, the original Clue
Logic class runs simultanenously. Once Super Crazy Broomhead finds anf
incongruity in the counting logic, it will return to the original Clue Logic
used by Crazy Broomhead. This might be the only reason that Group1Player2
dominates all games that have more than two players.
Super Crazy Broomhead is not used by Group1Player2 for two-player games. In these two-player games, Group1Player2 did relatively well. We probably should be grateful for our end-game interrogation strategy.
Like Super Crazy Broomhead, Psychotic Broomhead is a branch from Crazy
Broomhead. Psychotic Broomhead made the fairly conservative assumption
that once another player, , has interrogated yet another player,
, and finds that
owns card,
, it is unlikely that
will ask
about
again. If
asks
about
again,
should probably return
. Ultimately,
basically will
gain nothing from a second interrogation which includes
. If the
previous assumption holds true, with enough interrogations conducted by
every player combination and the absolute information we derived from
our own interrogations and the false interrogations, we should have a
decent idea of which cards are owned by which players.
Psychotic Broomhead maintains a by
table,
, for each
player,
. Each time
takes a turn, Psychotic Broomhead
ages each element in
except the cells which represent the cards
that
interrogated
about. For example, assuming Psychotic
Broomhead is
, if
asks
about cards,
and
acknowledges having a card,
, from that set, then every cell of
would be incremented except cells,
,
, and
,
which are reset to zero. Theoretically,
might interrogate
again and inevitably throw ``garbage'' into its interrogation to throw
off the other observing players. Since our assumption states that
should not include
,
will definitely age while all the other
cards will potentially be reset.
To further supplement this interpretation of other players' positive
interrogations, Psychotic Broomhead guesses the cards which
owns by collecting the
oldest cards from row
of each
where
and
. From this set of
cards, we select the
most frequently appearing
, where
represents the cards
which Psychotic Broomhead knows
definitely owns.
Psychotic Broomhead collects all the absolute truths that its counterpart,
Crazy Broomhead, infers. Thus, if Psychotic Broomhead makes an early
guess right before everyone else, it is only fairly uncertain about the
set of cards owned by one or two opponents. Clearly, this approach relies
heavily on a sufficient number of interrogations. This probabably occurs
in games with many players and many cards, with an inclination towards
a low .
|
While Psychotic Broomhead's performance paled in comparison to Super Crazy Broomhead's, both players served as interesting offshoots from the same basic player, Crazy Broomhead.
Table indicates that Super Crazy Broomhead's
assumptions proved to be very fruitful. When Super Crazy Broomhead did
not come in first, it usually fell right behind Psychotic Broomhead.
Perhaps, the more interesting results were the two player games with one or two cards per player. In those games, Super Crazy Broomhead performed average, which probably means other players were being more aggressive in guessing early or our Crazy Broomhead, our most conversative player whose sole purpose was to extract only the absolute truth, was not extracting enough information relative to the other players.
Table indicates that Psychotic Broomhead performed
significantly better with more cards than with few cards. We suspect
that Psychotic Broomhead threw Exceptions during games with greater than
eight cards. For games with greater than eight cards per player,
we clearly see its performance degrade from eight cards games to sixteen
cards games. Otherwise, with the exception of games with one card per
player, as the number of players per game increases, Psychotic Broomhead
gained enough data to make some fairly accurate guesses as to which
cards were owned by each player.
Ultimately, the players presented by each group clearly showed that the entire class was close to exhausting the number of inferences that could be derived from each of their respective interrogations. To gain an edge, assumptions that were consistent across all the opponents had to be made. Generally, excluding cheating, in any closely competed match where players' actions can affect both the performances and countermoves of other players, a reasonable analysis of how the other contestants' play will be the only way to score the decisive points.
In two-player games, the best solution for the worst permutation is to
interrogate the opponent for times, where
is the number of
cards held by each player. This can be proved by adversary - just
suppose the opponent can cheat by exchanging cards on hand with those
on stake and always respond positively. Therefore, whatever
interrogation strategy is used, there are always some permutations
such that one has to do
interrogations. Similarly we can also
prove
is the best solution for the worst permutation in
multi-player games, by adversary.
We now turn to study the optimal expected number of interrogations, since one cannot do better in the worst case. Only two-player games are discussed here for simplicity.
Suppose cards held by the opponent and
cards on the stake are
uncertain for our player. The goal of our player is to find the
optimal expectation of number of interrogations, and the optimal
number of cards it is going to ask in the next turn. Let
be the optimal expected number of interrogations, and
be the optimal expected number of interrogations on the
condition that we are going to ask for
cards in the next turn.
Clearly,
![]() |
(3) |
![]() |
(4) |
![]() |
(5) |
More mathematics can be done here; but this problem is now a static dynamic programming problem, whose solution is independent to
the permutation of the cards. So we wrote a program to solve the
problem. Entries of the following table, generated by our program,
indicate the expected numbers of interrogations and optimal numbers of
cards for the next interrogation for different s and
s.
![]() |
![]() |
|||||||||||||||||||
on hand | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ||||||||||
1 | 1.0/1 | 1.0/2 | 1.0/3 | 1.0/4 | 1.0/5 | 1.0/6 | 1.0/7 | 1.0/8 | 1.0/9 | 1.0/10 | ||||||||||
2 | 1.667/1 | 1.833/2 | 1.9/3 | 1.933/4 | 1.952/5 | 1.964/6 | 1.972/7 | 1.978/8 | 1.982/9 | 1.985/10 | ||||||||||
3 | 2.25/1 | 2.65/2 | 2.805/3 | 2.878/4 | 2.918/5 | 2.941/6 | 2.956/7 | 2.966/8 | 2.973/9 | 2.978/10 | ||||||||||
4 | 2.8/1 | 3.473/2 | 3.725/3 | 3.837/4 | 3.894/5 | 3.927/6 | 3.947/7 | 3.96/8 | 3.969/9 | 3.975/10 | ||||||||||
5 | 3.333/1 | 4.308/2 | 4.658/3 | 4.807/4 | 4.879/5 | 4.918/6 | 4.942/7 | 4.957/8 | 4.967/9 | 4.974/10 | ||||||||||
6 | 3.857/1 | 5.154/2 | 5.592/2 | 5.775/3 | 5.863/4 | 5.91/5 | 5.937/6 | 5.954/7 | 5.965/8 | 5.973/9 | ||||||||||
7 | 4.375/1 | 5.981/1 | 6.51/2 | 6.741/3 | 6.848/4 | 6.903/5 | 6.934/6 | 6.952/7 | 6.964/8 | 6.972/9 | ||||||||||
8 | 4.889/1 | 6.763/1 | 7.422/2 | 7.707/3 | 7.835/4 | 7.897/5 | 7.931/6 | 7.951/7 | 7.963/8 | 7.972/9 | ||||||||||
9 | 5.4/1 | 7.515/1 | 8.33/2 | 8.675/3 | 8.823/4 | 8.892/5 | 8.929/6 | 8.95/7 | 8.963/8 | 8.971/9 | ||||||||||
10 | 5.909/1 | 8.247/1 | 9.237/2 | 9.645/3 | 9.81/3 | 9.887/4 | 9.926/5 | 9.949/6 | 9.962/7 | 9.971/8 |
As we can see in the table, the optimal number of cards for the next
interrogation is less than or equal to . Our submitted players
would ask for exactly
cards for two-player games, which is almost
optimal, except for combinations of small
s and large
s. An
extreme case in this table is that one is better to ask for one card
instead of two, if there are only two cards on the stake and more than
six cards on the opponent's hand.
Finally, here is our program for this problem:
class OptQuest { static double binomial( int n, int c ) { if ( c > n - c ) c = n - c; double ret = 1; for ( int i=0; i<c; i++ ) { ret *= n - i; ret /= i + 1; } return ret; } static double alpha( int p, int q, int k ) { return binomial( q, k ) / binomial( p+q, k ); } static int solve( double[][] mat, int p, int q ) { int k = 0; double optx = 1E10; for ( int i=1; i<=p+q; i++ ) { double x; if ( i <= q ) { double alpha = alpha( p, q, i ); x = (1-alpha) * mat[p-1][q] + alpha * mat[p][q-i]; } else { x = mat[p-1][q]; } x++; if ( x < optx ) { optx = x; k = i; } } mat[p][q] = optx; return k; } static void solve( double[][] mat, int[][] k ) { int np = mat.length; int nq = mat[0].length; // Java initializes all elements to zero for ( int p=1; p<np; p++ ) for ( int q=1; q<nq; q++ ) k[p][q] = solve( mat, p, q ); } public static void main( String[] args ) { int np = 11, nq = 11; double[][] mat = new double[np][nq]; int[][] k = new int[np][nq]; solve( mat, k ); java.text.NumberFormat nf = java.text.NumberFormat.getInstance(); nf.setMinimumFractionDigits( 1 ); nf.setMaximumFractionDigits( 3 ); nf.setMinimumIntegerDigits( 1 ); for ( int p=1; p<np; p++ ) { System.out.print( String.valueOf(p) ); for ( int q=1; q<np; q++ ) System.out.print( " & " + nf.format(mat[p][q]) + "/" + k[p][q] ); System.out.println( " \\\\" ); } } }
This document was generated using the LaTeX2HTML translator Version 2002 (1.62)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -split 0 -noaddress -local_icons -dir html -mkdir report.tex
The translation was initiated by Hanhua Feng on 2003-11-06