import java.util.Random;
import java.io.*;

public class MonteCarlo {
	static double Tc = 2.26919;
	
	int N;
	int[][] s;
	double[] bf = new double[3];

	double temperature;
	
	/* Observables */
	double magnetisation;
	double energy;
	
	public static void main(String[] args) {
		// Define factor for temperature
		int factor = 1;
		
		MonteCarlo mc = new MonteCarlo();
		mc.setT(factor * Tc);
		
		/* Insert N as param */
		mc.init(100, true);

		
		try {
			// Initialize file writing mechanism
			File file = new File("daten.txt"); 
			FileWriter fw = new FileWriter(file); 
			BufferedWriter bw = new BufferedWriter(fw);
			
			mc.run(600, false, bw, 800);
			
			bw.close();
					
			System.out.println("Fertig.");	
		}
		catch (IOException e) {
			System.out.println(e.toString());
		}
	}
	/* steps: amout of steps
	 * visual: Type true as param to visualize the simulation 
	 * 		   (adjust the size of your console window to get 
	 * 		   a suitable (not-scrolling) view of the process)
	 * bw:	   obj to write file
	 * k:	   only the k-th value is stored (to reduce data)
	 */
			  
	private void run(int steps, boolean visual, 
		BufferedWriter bw, int k) throws IOException {
		for (int a=0; a<steps*N*N; a++) {
			spinflip();	
			
			// Write observables to Buffer
			if (a % k == 0) {
				bw.write(a + "	" + magnetisation + "	" + 
				energy + "\r\n"); 
			}
			
			if (visual) print_array(a);
		}
	}
	
	private void spinflip() {
		Random rnd = new Random();
		int x = rnd.nextInt(N-1);
		int y = rnd.nextInt(N-1);
		
		int e = s[x][y] * ( s[(x-1+N)%N][y] + s[(x+1+N)%N][y] + 
							s[x][(y-1+N)%N] + s[x][(y+1+N)%N] );
		if (e < 0 || rnd.nextDouble() < bf[e/2]) {
			magnetisation += -2 * s[x][y];  
			energy += 2 * e;			
			s[x][y] = -s[x][y];
		}
	}

	private void init(int N, boolean random) {
		/* init spin array, set observables to values
		 * corresponding to start state. */
		this.N = N;
		this.s = new int[N][N];
		
		int spinsum = 0;
		energy = 0;
		if (random) {
			Random rnd = new Random();
			for(int i=0;i<N;i++)
			for(int j=0;j<N;j++) { 
				s[i][j] = rnd.nextInt(2) * 2 - 1;
			}
		}
		else {
			for(int i=0;i<N;i++)
			for(int j=0;j<N;j++) s[i][j] = 1;
		}
		
		for(int i=0;i<N;i++)
		for(int j=0;j<N;j++) {
			spinsum += s[i][j];
			energy += 2* s[i][j] * ( s[(i-1+N)%N][j] + 
						 s[(i+1+N)%N][j] + 
						 s[i][(j-1+N)%N] + s[i][(j+1+N)%N] );
		}
		
		magnetisation = spinsum / N / N;
	}
	
	private void setT(double t) {
		temperature = t;
		bf[0] = 0.5;
		bf[1] = Math.exp(-4.0/t);
		bf[2] = Math.exp(-8.0/t);
	}
	
	private void print_array(int step) {
		StringBuffer buf = new StringBuffer("");
		for (int a=0;a<N;a++) {
			buf.append("-");
		}
		buf.append("\n");
		for (int y=0;y<N;y++) {
			buf.append("|");
			for (int x=0;x<N;x++) {
				if (s[x][y] == -1) {
					buf.append("#"); 
				}
				else {
					buf.append(" "); 
				}
			}
			buf.append("|\n"); 
		}
		for (int a=0;a<N;a++) {
			buf.append("-");
		}
		buf.append("\n");
		buf.append("Simulation Step: " + step + "  M=" + 
				magnetisation + ", E=" + energy + "\n\n");
		System.out.print(buf.toString());
	}

}

