views:

100

answers:

1

I had a task to write simple game simulating two players picking up 1-3 matches one after another until the pile is gone. I managed to do it for computer choosing random value of matches but now I'd like to go further and allow humans to play the game. Here's what I already have : http://paste.pocoo.org/show/200660/

Class Player is a computer player, and PlayerMan should be human being. Problem is, that thread of PlayerMan should wait until proper value of matches is given but I cannot make it work this way. When I type the values it sometimes catches them and decrease amount of matches but that's not exactly what I was up to :) Logics is : I check the value of current player. If it corresponds to this of the thread currently active I use scanner to catch the amount of matches. Else I wait one second (I know it's kinda harsh solution, but I have no other idea how to do it).
Class Shared keeps the value of current player, and also amount of matches.

By the way, is there any way I can make Player and Shared attributes private instead of public and still make the code work ?

CONSOLE and INPUT-DIALOG is just for choosing way of inserting values.

class PlayerMan extends Player{

    static final int CONSOLE=0;
    static final int INPUT_DIALOG=1;
    private int input;

    public PlayerMan(String name, Shared data, int c){
    super(name, data);
    input = c;
    }

@Override
    public void run(){
        Scanner scanner = new Scanner(System.in);
        int n = 0;

        System.out.println("Matches on table: "+data.matchesAmount);
        System.out.println("which: "+data.which);
        System.out.println("number: "+number);

        while(data.matchesAmount != 0){
            if(number == data.which){

                System.out.println("Choose amount of matches (from 1 to 3): ");
                n = scanner.nextInt();

                if(data.matchesAmount == 1){
                    System.out.println("There's only 1 match left !");
                    while(n != 1){
                        n = scanner.nextInt();
                    }
                }
                else{
                    do{
                        n = scanner.nextInt();
                    }
                    while(n <= 1 && n >= 3);
                }

                data.matchesAmount = data.matchesAmount - n;
                System.out.println("                          "+
                        name+" takes "+n+" matches.");
                if(number != 0){
                    data.which = 0;
                }
                else{
                    data.which = 1;
                }
            }
            else{
                try {
                    Thread.sleep(1000);
                } catch(InterruptedException exc) {
                    System.out.println("End of thread.");
                    return;
                }
            }
            System.out.println("Matches on table: "+data.matchesAmount);
        }
        if(data.matchesAmount == 0){
            System.out.println("Winner is player: "+name);
            stop();
        }
    }
}
+1  A: 

You should synchronize with reader/writer locks. EDIT: On a second thought, perhaps a simple condition should be enough. You could use something like this:

private Lock playerLock = new ReentrantLock();
private Condition[] playerConditions = { playerLock.newCondition(), playerLock.newCondition() };

// Later on:
while (data.matchesAmount != 0) {
  while (number != data.which) {
    playerConditions[number].await();
  }
  // do work here

  // now release the other player -- this assumes there are only 2
  data.which = 1 - number;
  playerConditions[1 - number].signalAll();
}

The only problem here is that you may block both threads waiting for their conditions if data.which is not properly initialized when they get here. You should make sure it is initialized before starting their threads.

Daniel
I've just found something about this, also wait() method. So should I add method for writing number of matches synchronize it and insert into run()?
owca
@owca Basically, any data that is read or written by more than one thread should be synchronized. You may use the `synchronized` keyword around methods that do so, you may use `synchronized (object) { ... }` inside a method, you may use locks, or you may use conditions (shown in the updated answer). Never try to just use a variable, because data is not guaranteed to be consistent between threads without a lock of some kind.
Daniel