Created
June 18, 2019 17:31
-
-
Save icsaba/e8159f94f5105e6fc19da269a51e05e6 to your computer and use it in GitHub Desktop.
EAK 2 Exam, Game of Life
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package obsxvv; | |
import java.util.concurrent.*; | |
/** Game of Life. */ | |
public class Life { | |
/** Invariant: <code> | |
from != null & to != null & | |
from.length == to.length != 0 & | |
from[i] != null & to[i] != null & | |
from[i].length == to[j].length != 0 </code> (for all sensible <code>i,j</code>) | |
*/ | |
protected boolean[][] from, to; | |
private ExecutorService executorService; | |
private CyclicBarrier barrier; | |
private int chunkSize; | |
private int threads; | |
/** Whether the world is a torus. | |
Otherwise cells die ouside of the world. | |
*/ | |
protected boolean torus; | |
/** A new n x m field with no living cells. */ | |
public Life( int n, int m, boolean torus , int threads){ | |
if( n <= 0 || m <= 0 ) throw new IllegalArgumentException(); | |
from = new boolean[n][m]; | |
to = new boolean[n][m]; | |
this.torus = torus; | |
executorService = Executors.newFixedThreadPool(threads); | |
barrier = new CyclicBarrier(threads); | |
chunkSize = from.length / threads; | |
this.threads = threads; | |
} | |
/** Plays <code>count</code> generations of Game of Life. */ | |
public void iterate( int count ){ | |
try { | |
while( count > 0 ){ | |
step(); | |
System.out.println("count : " + count); | |
boolean[][] tmp = from; from = to; to = tmp; // swap from and to | |
--count; | |
} | |
} finally { | |
executorService.shutdown(); | |
} | |
} | |
/** Compute new state from the array <code>from</code> to the array <code>to</code>. */ | |
protected void step(){ | |
for (int i = 0; i < this.threads; i++) { | |
int threadIndex = i; | |
executorService.execute( | |
() -> { | |
int startIndex = threadIndex * chunkSize; | |
for (int j = startIndex; j < startIndex + chunkSize && j < from.length; j++) { | |
for (int k = 0; k < from[j].length; k++) { | |
switch( living9(j,k) ){ | |
case 3: to[j][k] = true; break; | |
case 4: to[j][k] = from[j][k]; break; | |
default: to[j][k] = false; | |
} | |
} | |
} | |
try { | |
barrier.await(); | |
} catch (InterruptedException | BrokenBarrierException e) { | |
e.printStackTrace(); | |
} | |
} | |
); | |
} | |
for(boolean[] row : to){ | |
for(boolean item: row){ | |
System.out.print( item ? "\\o/" : ' '); | |
} | |
System.out.println(); | |
} | |
} | |
/** Count living cells in a 3x3 block with center <code>(n,m)</code>. */ | |
public int living9( int n, int m ){ | |
int living = 0; | |
for(int i=-1; i<=1; ++i) | |
for(int j=-1; j<=1; ++j) | |
if( get(n+i,m+j) ) | |
++living; | |
return living; | |
} | |
/** Retrive cell. Depends on whether the world is a torus. */ | |
public boolean get( int n, int m ){ | |
return torus ? torus(n,m) : deadEnvironment(n,m); | |
} | |
private boolean torus( int n, int m ){ | |
n = (n+from.length) % from.length; | |
m = (m+from[0].length) % from[0].length; | |
return from[n][m]; | |
} | |
private boolean deadEnvironment( int n, int m ){ | |
return n >= 0 && m >= 0 && n < from.length && m < from[0].length | |
&& from[n][m]; | |
} | |
///////////////////////////////////////// | |
// Put some well-known patterns if a life | |
///////////////////////////////////////// | |
/** Put a Blinker at position <code>(n,m)</code>. */ | |
public Life blinker(int n, int m){ | |
from[n+2][m+1] = from[n+2][m+2] = from[n+2][m+3] = true; | |
return this; | |
} | |
/** Put an Acorn at position <code>(n,m)</code>. */ | |
public Life acorn(int n, int m){ | |
from[n+1][m+2] = from[n+2][m+4] = from[n+3][m+1] = from[n+3][m+2] = from[n+3][m+5] = from[n+3][m+6] = from[n+3][m+7] = true; | |
return this; | |
} | |
/** Put an "Infinite Growth with 10 cells" at position <code>(n,m)</code>. */ | |
public Life inf10(int n, int m){ | |
from[n+2][m+7] = from[n+4][m+6] = from[n+4][m+7] = from[n+6][m+3] = from[n+6][m+4] = from[n+6][m+5] = from[n+8][m+2] = from[n+8][m+3] = from[n+8][m+4] = from[n+9][m+3] = true; | |
return this; | |
} | |
/** Put a Gosper Glider Gun at position <code>(n,m)</code>. */ | |
public Life gosperGliderGun(int n, int m){ | |
from[n+1][m+5] = from[n+1][m+6] = from[n+2][m+5] = from[n+2][m+6] = from[n+11][m+5] = from[n+11][m+6] = from[n+11][m+7] = from[n+12][m+4] = from[n+12][m+8] = from[n+13][m+3] = from[n+13][m+9] = from[n+14][m+3] = from[n+14][m+9] = from[n+15][m+6] = from[n+16][m+4] = from[n+16][m+8] = from[n+17][m+5] = from[n+17][m+6] = from[n+17][m+7] = from[n+18][m+6] = from[n+21][m+3] = from[n+21][m+4] = from[n+21][m+5] = from[n+22][m+3] = from[n+22][m+4] = from[n+22][m+5] = from[n+23][m+2] = from[n+23][m+6] = from[n+25][m+1] = from[n+25][m+2] = from[n+25][m+6] = from[n+25][m+7] = from[n+35][m+3] = from[n+35][m+4] = from[n+36][m+3] = from[n+36][m+4] = true; | |
return this; | |
} | |
//////////////////////////////// | |
// Input-output from/to textfile | |
//////////////////////////////// | |
public Life fromFile( String fname ) throws java.io.IOException { | |
try( | |
java.util.Scanner scanner = new java.util.Scanner(new java.io.File(fname)); | |
){ | |
while( scanner.hasNext() ) | |
from[ scanner.nextInt() ][ scanner.nextInt() ] = true; | |
return this; | |
} | |
} | |
public void toFile( String fname ) throws java.io.IOException { | |
System.out.println("wrinting file"); | |
try( | |
java.io.PrintWriter printer = new java.io.PrintWriter(new java.io.File(fname)); | |
){ | |
for( int i=0; i<from.length; ++i ){ | |
for( int j=0; j<from[0].length; ++j ){ | |
if( from[i][j] ){ | |
printer.println(i + " " +j); | |
} | |
} | |
} | |
} | |
} | |
///////////////////////////////////////////////////////// | |
// Animation to visualize Game of Life on standard output | |
///////////////////////////////////////////////////////// | |
public void animate( int iterations, int steps, int delay, int row, int col, int rows, int cols ){ | |
String line; { | |
StringBuilder builder = new StringBuilder(); | |
for( int i=0; i<cols; ++i ) builder.append('-'); | |
builder.append('+'); | |
line = builder.toString(); | |
} | |
print(row,col,rows,cols,line); | |
for( int i = 0; i < iterations; i+= steps ){ | |
iterate(steps); | |
print(row,col,rows,cols,line); | |
try {Thread.sleep(delay);} catch (InterruptedException e){} | |
} | |
} | |
private void print( int row, int col, int rows, int cols, String line ){ | |
for( int i=row; i<row+rows; ++i ){ | |
for( int j=col; j<col+cols; ++j ){ | |
System.out.print(from[i][j]?'@':' '); | |
} | |
System.out.println("|"); | |
} | |
System.out.println(line); | |
} | |
/////////////////////////// | |
// Measuring execution time | |
/////////////////////////// | |
public long measure( int iterations ){ | |
long startTime = System.currentTimeMillis(); | |
// executorService.execute(this::userInput); | |
iterate(iterations); | |
long endTime = System.currentTimeMillis(); | |
return endTime-startTime; | |
} | |
/////// | |
// main | |
/////// | |
public static void main( String[] args ) throws Exception { | |
if( args.length < 5 ){ | |
// Life life = new Life(20,50,false, 5); | |
// new Life(50,50,false).acorn(25,25).animate(200,1,100,0,0,50,50); | |
// new Life(500,500,false).inf10(5,20).animate(100,1,100,0,0,20,50); | |
// new Life(500,500,false, 10).gosperGliderGun(5,5).animate(200,1,100,0,0,50,80); | |
Life life = new Life(300,300,false, 5); | |
// life.gosperGliderGun(5,5).animate(111,5,100,0,0,50,80); | |
System.out.println("Computation lasted "+ life.gosperGliderGun(0,0).measure(5) +" milliseconds."); | |
life.toFile("output.dat"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment