CSC352 Game of Life, 2 Threads

From dftwiki3
Revision as of 15:44, 28 February 2017 by Thiebaut (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

--D. Thiebaut (talk) 15:40, 28 February 2017 (EST)


Threaded Game of Life in Java


The following code uses 2 threads to compute all the generations of the game of life. Only the first and last generations are printed. The serial version of the Game of Life in Java is available here.

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/*  
 Game of life
 D. Thiebaut
 Heavily adapted from code found in java section at this
 URL:  https://rosettacode.org/wiki/Conway%27s_Game_of_Life#Java

 This code works in console mode, displaying successive generations
 of the game of life on the screen, and clearing the screen between
 each one.

 The initial pattern is defined in the array dish (as in petri dish).

 To compile and run:

 javac GameOfLife.java
 java GameOfLife


*/

class data {
    static String[] dish= {
        "                                                                                  ",
        "   #                                                                              ",
        " # #                                            ###                               ",
        "  ##                                                                              ",
        "                                                                                  ",
        "                                                      #                           ",
        "                                                    # #                           ",
        "                                                     ##                           ",
        "                                                                                  ",
        "                                                                                  "
    };
    
    static String[] dish2= {
        "                                                                                  ",
        "   #                                                                              ",
        " # #                                            ###                               ",
        "  ##                                                                              ",
        "                                                                                  ",
        "                                                      #                           ",
        "                                                    # #                           ",
        "                                                     ##                           ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "             #                                                                    ",
        "           # #                                                                    ",
        "            ##                                                                    ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  ",
        "                                                                                  "
    };	
}


/*
 * This is the thread class that will work on 1/2 of the
 * dish array.  If Id == 0, then work on low rows,
 * if Id == 1, then work on high rows.
 */
class MyThread extends Thread{
	int Id;  // the Id of the thread, 0, or 1.
	int gens = 3000;
	BlockingQueue<Integer> sendQ;
	BlockingQueue<Integer> receiveQ;
	
	MyThread( int Id, int gens, BlockingQueue<Integer> sendQ, 
								BlockingQueue<Integer> receiveQ ) {
		this.Id = Id;
		this.gens = gens;
		this.sendQ = sendQ;
		this.receiveQ = receiveQ;
	}
	
    /*
     * This will apply life repeatedly to the half of the
     * dish array that belongs to this thread.
     * @see java.lang.Thread#run()
     */
	public void run(){  
        int midRow =  data.dish.length / 2;
        
        for (int i= 0;i < gens;i++) {
            //System.out.println("Generation " + i + ":\n\n\n");
        	String[] newGen;
	        if ( Id==0 ) 
	        	newGen = life( 0, midRow );
	        else
	        	newGen = life( midRow, data.dish.length );
	        
	        // wait for life to have finished.
	        // send a token, wait for token
	        try {
		        sendQ.put( 1 );
				receiveQ.take( );
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
	        
	        // now safe to copy new generation into dish
	        if ( Id==0 ) 
	        	copyDish( newGen, 0, midRow  );
	        else
	        	copyDish( newGen, midRow, data.dish.length );

	        // wait for copy operation to be done.
	        // send a token, wait for token
	        try {
		        sendQ.put( 1 );
				receiveQ.take( );
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
        }        
    }  
	
	private static void copyDish( String[] newGen, int startRow, int endRow ) {
        // copy newGen into dish
        for ( int row= startRow; row < endRow; row++ ) {
        	data.dish[row] = newGen[row];
        }

	}
    private static String[] life( int startRow, int endRow ){
        /*
        Given an array of string representing the current population of
        cells in a petri dish, computes the new generation of cells according
        to the rules of the game.
        A new array of strings is returned.
        */

        String[] newGen= new String[ data.dish.length ];

        for ( int row= startRow; row < endRow; row++ ) {//each row

            newGen[row]= "";

            for ( int i= 0; i < data.dish[row].length(); i++ ) {//each char in the row

                int neighbors = 0;
                char current = data.dish[row].charAt(i);

                // loop in a block that is 3x3 around the current cell
                // and count the number of '#' cells.  
                for ( int r=row-1; r<=row+1; r++ )  {

                    // make sure we wrap around from bottom to top
                    int realr = r;
                    if ( r==-1 )         realr = data.dish.length-1;
                    if ( r==data.dish.length) realr = 0;

                    for ( int j=i-1; j<=i+1; j++ ) {
                        
                        // make sure we wrap around from left to right
                        int realj = j;
                        if ( j==-1 )                 realj = data.dish[row].length()-1;
                        if ( j==data.dish[row].length() ) realj = 0;

                        if (r==row && j==i ) continue; // current cell is not its
                                                       // neighbor
                        if (data.dish[realr].charAt(realj) == '#' )
                            neighbors++;
                    }
                }

                if ( current=='#' ) 
                    if (neighbors < 2 || neighbors > 3) 
                        newGen[row] +=  " "; 
                    else
                        newGen[row] += "#";  

                if ( current==' ' )
                    if ( neighbors == 3 )
                        newGen[row] += "#";
                    else
                        newGen[row] += " ";
            }
        }
        return newGen;
        
    }

}

public class GameOfLife2Threads{
	// static variables are global to all objects instanciated from the class.
	
    public static void main(String[] args){
    	// display original generation
    	print( data.dish );
    	
    	BlockingQueue<Integer> queue01 = new ArrayBlockingQueue<Integer>(2);
    	BlockingQueue<Integer> queue10 = new ArrayBlockingQueue<Integer>(2);
    	
    	// create 2 workers
    	MyThread t0=new MyThread( 0, 3000, queue01, queue10 );
    	MyThread t1=new MyThread( 1, 3000, queue10, queue01 );
    	
    	// start 2 workers 
    	t0.start();
    	t1.start();
    	
    	// wait for the 2 threads to be done
        try {
            t1.join();
            t0.join();
        }
        catch (InterruptedException ie) {
            ie.printStackTrace();
        }
        
        // display final generation
        print( data.dish );
    }
	
    public static void clearScreen(){
        /* 
        brings the cursor home (top-left), so that the 
        next generation will be printed over the current one.
        */
        final String ANSI_CLS    = "\u001b[2J";
        final String ANSI_HOME = "\u001b[H";
        System.out.print( ANSI_HOME);
        System.out.flush();
    }
            
    public static void print( String[] dish ) {
        /*
        just display all the lines of the array of strings.
        */
        clearScreen();
        System.out.println( new String(new char[dish[0].length()] ).replace('\0', '-' ) );

        for ( String s: dish )
            System.out.println(s);
        
    }
}