Solutions for Problem set 1

This page contains partial solutions for the problem sets. The solutions here don't have the complete functionality of the problem, but illustrate the most important points.

Code for both programs is available to download in /home/ecom/public_html/download.

Problems 2 and 3

This program implements simulation of a poker game using threads. The game goes for 10 rounds. The winner of each round is determined, but no money is implemented. The program throws an exception if there is not enough cards in the deck for the next player.

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!";
    }
}

Solution for problem 4: file reading

This program creates a very uninspiring graphical user interface (many of you did a much better job on it!). User can enter the file name, the file is read 10 lines at a time. It doesn't display an error message if the file is not found or if there is a mistake in reading, but it displays a message "That's all, folks!" at the end of the file.


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!


This page has been created and is maintained by Elena Machkasova
Comments and suggestions are welcome at emachkas@wellesley.edu

Spring Semester 2002