A simple Game of Life implementation in Java
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

363 lignes
12 KiB

  1. package edu.stuy.goldfish;
  2. import java.util.Random;
  3. import java.util.concurrent.atomic.AtomicBoolean;
  4. import java.util.concurrent.atomic.AtomicInteger;
  5. import java.awt.*;
  6. import java.awt.event.*;
  7. import java.awt.image.*;
  8. import javax.swing.*;
  9. import javax.swing.event.*;
  10. public class Render extends Canvas implements Runnable, MouseListener,
  11. MouseMotionListener, ActionListener {
  12. private static final long serialVersionUID = 1L;
  13. public static String title;
  14. public static int width;
  15. public static int height;
  16. public static int scale;
  17. public int fps_now;
  18. public boolean paused;
  19. public String rule;
  20. public boolean reset;
  21. private AtomicBoolean[] _flags;
  22. private AtomicInteger _turn;
  23. private Grid _grid;
  24. private int[] _pixels;
  25. private BufferedImage _image;
  26. private long _lastTick;
  27. private String[] _rules;
  28. private JFrame _frame;
  29. private JButton pauseButton;
  30. private Random random = new Random();
  31. private int _drawState;
  32. public Render(int width, int height, Grid g, String[] rules) {
  33. addMouseListener(this);
  34. addMouseMotionListener(this);
  35. Render.title = "Goldfish: " + rules[0];
  36. Render.width = width;
  37. Render.height = height;
  38. setScale();
  39. paused = false;
  40. reset = false;
  41. rule = rules[0];
  42. _flags = new AtomicBoolean[2];
  43. for (int i = 0; i < _flags.length; i++)
  44. _flags[i] = new AtomicBoolean();
  45. _turn = new AtomicInteger();
  46. _grid = g;
  47. _image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
  48. _pixels = ((DataBufferInt) _image.getRaster().getDataBuffer()).getData();
  49. _lastTick = 0;
  50. _rules = rules;
  51. _drawState = -1;
  52. fps_now = 15;
  53. setFrame();
  54. }
  55. public Render(int width, int height, Grid g) {
  56. this(width, height, g, new String[0]);
  57. }
  58. public Render(int width, int height) {
  59. this(width, height, new Grid(width, height));
  60. }
  61. public Render() {
  62. this(256, 256);
  63. }
  64. /* Set the grid's scale factor so smaller grids appear larger. */
  65. private void setScale() {
  66. if (height <= 128 || width <= 128) {
  67. Render.scale = 4;
  68. } else if (height <= 256 || width <= 256) {
  69. Render.scale = 2;
  70. } else {
  71. Render.scale = 1;
  72. }
  73. }
  74. /* Set up the window frame with various buttons. */
  75. private void setFrame() {
  76. JMenuBar menuBar = new JMenuBar();
  77. JMenu menuAlgo = new JMenu("Algorithms");
  78. menuAlgo.setFont(new Font("Arial", 1, 12));
  79. menuAlgo.setPreferredSize(new Dimension(75, 0));
  80. for (String rule : _rules) {
  81. JMenuItem menuAlgoItem = new JMenuItem(rule);
  82. menuAlgo.add(menuAlgoItem);
  83. menuAlgoItem.addActionListener(this);
  84. }
  85. menuBar.add(menuAlgo);
  86. pauseButton = new JButton("Pause");
  87. pauseButton.setActionCommand("pause");
  88. pauseButton.setFont(new Font("Arial", 0, 12));
  89. pauseButton.setPreferredSize(new Dimension(90, 0));
  90. menuBar.add(pauseButton);
  91. pauseButton.addActionListener(this);
  92. JButton resetButton = new JButton("Reset");
  93. resetButton.setActionCommand("reset");
  94. resetButton.setFont(new Font("Arial", 0, 12));
  95. menuBar.add(resetButton);
  96. resetButton.addActionListener(this);
  97. JButton randomButton = new JButton("Random");
  98. randomButton.setActionCommand("random");
  99. randomButton.setFont(new Font("Arial", 0, 12));
  100. menuBar.add(randomButton);
  101. randomButton.addActionListener(this);
  102. JButton clearButton = new JButton("Clear");
  103. clearButton.setActionCommand("clear");
  104. clearButton.setFont(new Font("Arial", 0, 12));
  105. menuBar.add(clearButton);
  106. clearButton.addActionListener(this);
  107. JSlider framesPerSecond = new JSlider(JSlider.HORIZONTAL, 0, 30, 15);
  108. ChangeListener fpschange = new ChangeListener() {
  109. @Override
  110. public void stateChanged(ChangeEvent event) {
  111. JSlider source = (JSlider) event.getSource();
  112. if (!source.getValueIsAdjusting()) {
  113. int fps = (int) source.getValue();
  114. if (fps == 0) {
  115. paused = true;
  116. pauseButton.setText("Unpause");
  117. } else {
  118. paused = false;
  119. fps_now = fps;
  120. pauseButton.setText("Pause");
  121. }
  122. }
  123. }
  124. };
  125. framesPerSecond.addChangeListener(fpschange);
  126. framesPerSecond.setMajorTickSpacing(10);
  127. framesPerSecond.setMinorTickSpacing(1);
  128. framesPerSecond.setPaintTicks(true);
  129. framesPerSecond.setPaintLabels(true);
  130. framesPerSecond.setFont(new Font("Arial", 0, 12));
  131. framesPerSecond.setPreferredSize(new Dimension(100, 0));
  132. menuBar.add(framesPerSecond);
  133. menuBar.setPreferredSize(new Dimension(width * scale, 40));
  134. setPreferredSize(new Dimension(width * scale, height * scale));
  135. _frame = new JFrame();
  136. _frame.setJMenuBar(menuBar);
  137. _frame.setResizable(false);
  138. _frame.setTitle(title);
  139. _frame.add(this);
  140. _frame.pack();
  141. _frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  142. _frame.setLocationRelativeTo(null);
  143. _frame.setVisible(true);
  144. }
  145. /* Actually draw the grid to the screen. */
  146. private void update() {
  147. int state;
  148. int states = Goldfish.getMaxStates(rule);
  149. for (int i = 0; i < width; i++) {
  150. for (int j = 0; j < height; j++) {
  151. state = _grid.getPatch(i, j).getState();
  152. draw(i, j, (int) ((state / ((double) states - 1)) * 0xffffff));
  153. }
  154. }
  155. }
  156. /* Acquire a lock so two threads don't modify the grid at the same time. */
  157. public void acquireLock(int thread) {
  158. int other = (thread == 0 ? 1 : 0);
  159. _flags[thread].set(true);
  160. while (_flags[other].get() == true) {
  161. if (_turn.get() != thread) {
  162. _flags[thread].set(false);
  163. while (_turn.get() != thread) {}
  164. _flags[thread].set(true);
  165. }
  166. }
  167. }
  168. /* Release the lock when a thread is finished modifying the grid. */
  169. public void releaseLock(int thread) {
  170. int other = (thread == 0 ? 1 : 0);
  171. _turn.set(other);
  172. _flags[thread].set(false);
  173. }
  174. /* Main method to render the grid at its current state. */
  175. public void run() {
  176. BufferStrategy bs;
  177. Graphics g;
  178. bs = getBufferStrategy();
  179. if (bs == null) {
  180. createBufferStrategy(1);
  181. return;
  182. }
  183. update();
  184. g = bs.getDrawGraphics();
  185. g.drawImage(_image, 0, 0, getWidth(), getHeight(), null);
  186. g.dispose();
  187. bs.show();
  188. }
  189. /* Maintain a maximum FPS. */
  190. public void sleep() {
  191. long since = System.currentTimeMillis() - _lastTick;
  192. if (since < 1000 / fps_now) {
  193. try {
  194. Thread.sleep(1000 / fps_now - since);
  195. } catch (InterruptedException e) {
  196. return;
  197. }
  198. }
  199. _lastTick = System.currentTimeMillis();
  200. }
  201. /* Set the grid to be rendered. */
  202. public void setGrid(Grid g) {
  203. _grid = g;
  204. }
  205. /* Draw a certain color at (x, y) to the screen. */
  206. public void draw(int x, int y, int color) {
  207. if (_pixels[x + y * width] != color)
  208. _pixels[x + y * width] = color;
  209. }
  210. /* Set the state of all patches in the grid to zero. */
  211. public void clear() {
  212. acquireLock(1);
  213. for (int i = 0; i < _grid.getWidth(); i++) {
  214. for (int j = 0; j < _grid.getHeight(); j++) {
  215. _grid.getPatch(i, j).setState(0);
  216. }
  217. }
  218. releaseLock(1);
  219. }
  220. /* Set each patch in the grid to a random state. */
  221. public void randomize() {
  222. acquireLock(1);
  223. for (int i = 0; i < _grid.getWidth(); i++) {
  224. for (int j = 0; j < _grid.getHeight(); j++) {
  225. _grid.getPatch(i, j).setState(random.nextInt(Goldfish.getMaxStates(rule)));
  226. }
  227. }
  228. releaseLock(1);
  229. }
  230. /* Handle a mouse event by modifying the patch the mouse is over. */
  231. private void mouseDraw(MouseEvent e) {
  232. int states = Goldfish.getMaxStates(rule);
  233. if (e.getX() < 0 || e.getY() < 0 || e.getX() / scale >= width || e.getY() / scale >= height)
  234. return;
  235. Patch p = _grid.getPatch(e.getX() / scale, e.getY() / scale);
  236. if (_drawState == -1) {
  237. if (p.getState() == 0) {
  238. if (SwingUtilities.isLeftMouseButton(e))
  239. _drawState = states - 1;
  240. else if (SwingUtilities.isRightMouseButton(e))
  241. _drawState = 1;
  242. } else {
  243. _drawState = 0;
  244. }
  245. }
  246. p.setState(_drawState);
  247. e.consume();
  248. }
  249. /* Called when the mouse is dragged over a pixel. */
  250. @Override
  251. public void mouseDragged(MouseEvent e) {
  252. mouseDraw(e);
  253. }
  254. /* Called whenever the mouse is clicked. */
  255. @Override
  256. public void mouseClicked(MouseEvent e) {
  257. }
  258. /* Called whenever the mouse enters the window. */
  259. @Override
  260. public void mouseEntered(MouseEvent e) {
  261. }
  262. /* Called whenever the mouse exits the window. */
  263. @Override
  264. public void mouseExited(MouseEvent e) {
  265. }
  266. /* Called whenever the mouse is pressed. */
  267. @Override
  268. public void mousePressed(MouseEvent e) {
  269. mouseDraw(e);
  270. }
  271. /* Called whenever the mouse is released from being pressed. */
  272. @Override
  273. public void mouseReleased(MouseEvent e) {
  274. _drawState = -1;
  275. }
  276. /* Called whenever the mouse is moved, pressed or not. */
  277. @Override
  278. public void mouseMoved(MouseEvent e) {
  279. }
  280. /* Hook to handle button presses and menu choices. */
  281. @Override
  282. public void actionPerformed(ActionEvent event) {
  283. if ("pause".equals(event.getActionCommand())) {
  284. if (paused) {
  285. paused = false;
  286. pauseButton.setText("Pause");
  287. } else {
  288. paused = true;
  289. pauseButton.setText("Unpause");
  290. }
  291. } else if ("reset".equals(event.getActionCommand())) {
  292. clear();
  293. reset = true;
  294. } else if ("random".equals(event.getActionCommand())) {
  295. randomize();
  296. } else if ("clear".equals(event.getActionCommand())) {
  297. clear();
  298. } else {
  299. String oldRule = rule;
  300. rule = event.getActionCommand();
  301. if (oldRule.equals("Brian's Brain") && !rule.equals("Brian's Brain")) {
  302. for (int i = 0; i < _grid.getWidth(); i++) {
  303. for (int j = 0; j < _grid.getHeight(); j++) {
  304. if (_grid.getPatch(i,j).getState() == 2)
  305. _grid.getPatch(i,j).setState(1);
  306. }
  307. }
  308. } else if (!oldRule.equals("Brian's Brain") && rule.equals("Brian's Brain")) {
  309. for (int i = 0; i < _grid.getWidth(); i++) {
  310. for (int j = 0; j < _grid.getHeight(); j++) {
  311. if (_grid.getPatch(i,j).getState() == 1)
  312. _grid.getPatch(i,j).setState(2);
  313. }
  314. }
  315. }
  316. title = "Goldfish: " + rule;
  317. _frame.setTitle(title);
  318. }
  319. }
  320. }