Code for both programs is available to download in /home/ecom/public_html/download.
Warning: this is just one possible solution. There are many things that I would have improved on if I had more time. However, this program implements the most important part of the assignment, i.e. the synchronization mechanism: all players get different cards because cards are dealt in a synchronized method, and the cards comparison happens after all the players have taken cards.
File Poker.java creates the deck and the player threads, starts the threads, and waits for the to finish. The last part is necessary to guarantee that the main thread does not exit before the player threads. This version creates 12 players to test the exception.
import java.util.*;
import java.io.*;
public class Poker {
public static void main (String [] args) {
Deck deck = new Deck(12);
Thread players[] = new Thread[12];
// create new threads
players[0] = new Player(0, "Alexandra", deck);
players[1] = new Player(1, "Bernard", deck);
players[2] = new Player(2, "Caroline", deck);
players[3] = new Player(3, "Daniel", deck);
players[4] = new Player(4, "Eugenia", deck);
players[5] = new Player(5, "Faye", deck);
players[6] = new Player(6, "George", deck);
players[7] = new Player(7, "Hermann", deck);
players[8] = new Player(8, "Irene", deck);
players[9] = new Player(9, "Jeremy", deck);
players[10] = new Player(10, "Katherine", deck);
players[11] = new Player(11, "Lauren", deck);
// starting threads
players[0].start();
players[1].start();
players[2].start();
players[3].start();
players[4].start();
players[5].start();
players[6].start();
players[7].start();
players[8].start();
players[9].start();
players[10].start();
players[11].start();
// Wait for the threads to finish
try {
players[0].join();
players[1].join();
players[2].join();
players[3].join();
players[4].join();
players[5].join();
players[6].join();
players[7].join();
players[8].join();
players[9].join();
players[10].join();
players[11].join();
} catch (InterruptedException e) {
System.out.println("Thread was interrupted");
}
}
}
File Player.java initializes the threads and makes them run for 10 rounds. Threads tend to run strictly in order in Linux, so we add random sleep time to make the threads run more randomly.
import java.util.*;
import java.io.*;
public class Player extends Thread {
int id;
Deck deck;
public Player (int i, String name, Deck thedeck) {
// invoke constructor of the class Thread which takes
// a String (the name of the thread) as a parameter. By default
// the constructor with no parameters would have been invoked.
super(name);
id = i;
deck = thedeck;
}
public void run() {
for (int i = 0; i < 10; ++i) {
try {
// sleep for a randomly chosen time
Thread.sleep( (int) Math.random() * 100);
} catch (InterruptedException e) {return;}
deck.getFive(id);
}
}
}
File Deck.java contains the main part of the program. Cards in a deck are marked to indicate which player has them in this round.
The class has one synchronized method getFive() which deals the cards to a player and performs the synchronization to compare the cards: if in the beginning of the method the deck has cards marked by the player's number, then the cards from the previous round have not been compared yet, and the player has to wait until some other thread compares the cards.
After the cards have been taken, a method compareCards() is invoked. In the beginning of the method the thread checks if it is the last one to take cards. If not, then the method returns without doing anything. Otherwise the thread compares the cards, clears the deck (i.e. marks all cards as not taken), and resets the player counter.
If the thread has reset the counter, then all threads are notified, and they all can procede to take cards for the next round.
import java.util.*;
public class Deck {
static final int HIGHCARD = 0;
static final int PAIR = 1;
static final int THREEKIND = 2;
static final int FOURKIND = 3;
static final int STRAIGHT = 4;
private Card cards[] = new Card[52];
private Random rndm;
private int num_players;
private int done;
private int winner;
private String names[];
public Deck (int n) {
for (int i = 0; i < 4; ++i) {
for (int j = 2; j < 15; ++j) {
cards[i*13 + j - 2] = new Card(i, j);
}
}
num_players = n;
done = 0;
names = new String[n];
//printDeck();
rndm = new Random(123456777);
}
public void printDeck() {
for (int i = 0; i < 52; ++i) {
cards[i].print();
}
}
public synchronized void getFive(int who) {
// save your name:
names[who] = new String((Thread.currentThread()).getName());
//System.out.println("get Five: " + who);
while (haveCards(who)) { // can't procede, the cards have not been compared
try { wait(); }
catch (InterruptedException e)
{ System.out.println("Thread was interrupted"); }
}
//checking if all cards are taken:
try {
if (lessThanFiveLeft()) // if yes, throw an exception
throw new AllCardsGoneException();
} catch (AllCardsGoneException e) {
System.out.print("EXCEPTION: ");
System.out.println(e);
System.exit(1);
}
//System.out.println("get Five past wait : " + who);
for (int i = 0; i < 5; ++i) {
dealCard(who);
}
//printCardsOf(who);
System.out.println(names[who] + " gets cards");
++done;
compareCards();
if (done == 0) // I have compared the cards and reset the counter
notifyAll();
}
// do I have cards in my hand?
public boolean haveCards(int who){
for (int i = 0; i < 52; ++i) {
if (cards[i].whoHas() == who)
return true;
}
return false;
}
public void compareCards() {
if (done == num_players) { // I am the last one, so I compare the cards
Hand [] hands = new Hand[5];
for (int i = 0; i < 5; ++ i) {
hands[i] = new Hand();
}
for (int i = 0; i < 52; ++ i) {
if ( cards[i].isTaken() ) {
hands[cards[i].whoHas()].add(i);
}
}
for (int i = 0; i < 5; ++ i) {
hands[i].figureOut();
System.out.println(names[i] + " has:");
for (int j = 0; j < 5; j++) {
cards[hands[i].getCard(j)].print();
}
//System.out.print("Player " + i + " has ");
switch (hands[i].what()) {
case STRAIGHT:
System.out.println("It's a STRAIGTH!!! WOW!");
break;
case FOURKIND:
System.out.println("It's four of a kind! I am in luck!");
break;
case THREEKIND:
System.out.println("It's three of a kind. Not bad!");
break;
case PAIR:
System.out.println("It's a pair. Better than nothing!");
break;
default: System.out.println("It's a high card. Oh, well...");
}
}
winner = chooseWinner(hands);
System.out.println("And the winner is... " + names[winner] + "!");
done = 0;
clearAll();
//System.out.println("I have cleared the deck");
}
// if I am not the last one, I do nothing
}
public int chooseWinner(Hand [] hands) {
int max = 0;
for (int i = 1; i < num_players; ++i) {
if (hands[i].isBetter(hands[max]))
max = i;
}
return max;
}
// i is the player
public void dealCard(int who) {
// choose a card
int i = Math.abs(rndm.nextInt()%52);
//System.out.println(i);
// while the card is taken,
// choose a card at random
while (cards[i].isTaken()) {
i = Math.abs(rndm.nextInt()%52);
//System.out.println(i);
}
// give the card to the player
cards[i].giveToPlayer(who);
}
public void clearAll() {
for (int i = 0; i < 52; ++i) {
cards[i].clear();
}
}
public void printCardsOf(int who) {
Thread me = Thread.currentThread();
System.out.println(me.getName() + " has cards:");
for (int i = 0; i < 52; ++i) {
if (cards[i].whoHas() == who) {
cards[i].print();
}
}
}
public boolean lessThanFiveLeft() {
int available = 0;
for (int i = 0; i < 52; ++i) {
if (!cards[i].isTaken())
++available;
}
if (available >= 5)
return false;
return true;
}
class Card {
private int suit; // from 0 to 3
private int value; // from 2 to 14
private int player_taken; // number of the player who has this card or -1
Card (int the_suit, int the_value) {
suit = the_suit;
value = the_value;
player_taken = -1; // no player has this card yet
}
void print() {
System.out.println(cardValue() + " of " + suitString());
}
int getValue() {
return value;
}
int getSuit() {
return suit;
}
String suitString() {
String to_return;
switch (suit) {
case 0: to_return = new String("Diamonds"); break;
case 1: to_return = new String("Clubs"); break;
case 2: to_return = new String("Hearts"); break;
case 3: to_return = new String("Spades"); break;
default: to_return = new String("Unknown suit") ;
}
return to_return;
}
String cardValue() {
String to_return;
switch (value) {
case 2: to_return = new String("2"); break;
case 3: to_return = new String("3"); break;
case 4: to_return = new String("4"); break;
case 5: to_return = new String("5"); break;
case 6: to_return = new String("6"); break;
case 7: to_return = new String("7"); break;
case 8: to_return = new String("8"); break;
case 9: to_return = new String("9"); break;
case 10: to_return = new String("10"); break;
case 11: to_return = new String("J"); break;
case 12: to_return = new String("Q"); break;
case 13: to_return = new String("K"); break;
case 14: to_return = new String("A"); break;
default: to_return = new String("Unknown value") ;
}
return to_return;
}
public void giveToPlayer(int i) {
player_taken = i;
}
public void clear() {
player_taken = -1;
}
public boolean isTaken() {
return (player_taken != -1);
}
public int whoHas() {
return player_taken;
}
// returns true if the current card is of greater value
// than card c
// assuming that the two cards aren't the same
public boolean greaterThan(Card c) {
if (value > c.value) return true;
if (value < c.value) return false;
if (suit > c.suit) return true;
return false;
}
}
private class Hand {
int kind;
int [] mycards;
int counter;
int start; // where a stretch of cards with the same value starts
Hand () {
kind = -1; // don't know yet
mycards = new int[5];
counter = 0;
start = -1;
}
void add (int card) {
//System.out.println(card);
mycards[counter] = card;
++counter;
}
int getCard(int i) {
return mycards[i];
}
int what() {
return kind;
}
void figureOut() {
// figure out the value of the hand, rearrange the mycards
order();
if (isStraight()) {
kind = STRAIGHT;
}
else if ( isThreeOrFourOfKind() ) { } // the method itself sets the kind
else if ( isPair() ) {
kind = PAIR;
}
else {
kind = HIGHCARD;
}
}
void order() {
for (int j = 0; j < 4; ++ j) {
// a very inefficient sorting algorithm, but it's very late now!
int max = j;
for (int i = j + 1; i < 5; ++ i) {
if (cards[mycards[i]].greaterThan(cards[mycards[max]]))
max = i;
}
int temp = mycards[j];
mycards[j] = mycards[max];
mycards[max] = temp;
}
}
// the cards are already ordered in decreasing order
boolean isStraight() {
for (int i = 0; i < 4; ++i) {
if (cards[mycards[i]].getValue()-cards[mycards[i+1]].getValue() != 1){
return false;
}
}
return true;
}
// cards are ordered. There may be only one such combination in the hand.
boolean isThreeOrFourOfKind() {
int count = 1;
int index = -1;
for (int i = 0; i < 4; i++) {
if (cards[mycards[i]].getValue() == cards[mycards[i+1]].getValue()){
++count;
if (index == -1) index = i;
} else if (index != -1) // we are beyound a stretch, there can't
// be another one
break;
}
if (count == 3) {
start = index;
kind = THREEKIND;
return true;
}
else if (count == 4) {
start = index;
kind = FOURKIND;
return true;
}
else
return false;
}
// assumptions: there is nothing higher than a pair in this hand,
// the cards are ordered.
// therefore the first pair that we find is the highest!
boolean isPair() {
for (int i = 0; i < 4; i++) {
if (cards[mycards[i]].getValue() == cards[mycards[i+1]].getValue()){
start = i;
return true;
}
}
return false;
}
// returns true if my hand is better than the other
boolean isBetter(Hand other) {
if (kind > other.kind)
return true;
else if (kind < other.kind)
return false;
else if (kind == STRAIGHT || kind == HIGHCARD)
return (cards[mycards[0]].greaterThan(cards[other.mycards[0]]));
else {
int my = start;
int her = other.start;
return (cards[mycards[my]].greaterThan(cards[other.mycards[her]]));
}
}
}
}
File AllCardsGoneException.java defines the exception:
import java.util.*;
public class AllCardsGoneException extends Exception {
public String toString() {
return "All cards are taken!";
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class ReadFile {
JTextArea output = new JTextArea(10, 50);
FileInputStream from;
JTextArea filename = new JTextArea(1,15);
public Component createComponents() {
JButton button = new JButton("Read");
button.setMnemonic(KeyEvent.VK_R);
// the button is activated when Alt-R is pressed
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
readMore();
//label.setText(labelPrefix + numClicks);
}
});
/*
* An easy way to put space between a top-level container
* and its contents is to put the contents in a JPanel
* that has an "empty" border.
*/
JPanel pane = new JPanel();
pane.setBorder(BorderFactory.createEmptyBorder(
30, //top
30, //left
10, //bottom
30) //right
);
pane.setLayout(new GridLayout(2, 1));
JPanel pane1 = new JPanel();
pane1.setLayout(new GridLayout(1, 3));
pane1.add(button);
pane1.add(new JLabel(" Please enter file name:"));
pane1.add(filename);
//pane1.add(new JLabel(""));
pane.add(pane1);
output.setLineWrap(true);
pane.add(output);
return pane;
}
public void openFile() throws FileNotFoundException {
String name = filename.getText();
from = new FileInputStream(name);
}
public void readMore() {
output.setText("");
if (from == null)
try {
openFile();
} catch (FileNotFoundException e) {
System.out.println(e);
return;
}
int counter = 10; // number of lines to write
int c; // stores a character
do {
try {
c = from.read();
} catch(IOException e) {
System.out.println(e);
return;
}
if (c == '\n') --counter;
if (c != -1) output.append(new String((new StringBuffer()).append((char) c)));
// isn't there an easier way to convert characters to strings?
} while (counter > 0 && c != -1);
if (c == -1) output.append("That's all, folks!");
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(
UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception e) { }
//Create the top-level container and add contents to it.
JFrame frame = new JFrame("ReadFile");
ReadFile rf = new ReadFile();
Component contents = rf.createComponents();
frame.getContentPane().add(contents, BorderLayout.CENTER);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.pack();
frame.setVisible(true);
}
}
Please let me know if you have any questions about the solutions!