Игра Жизнь Джона Конвея

 
 
 
Сообщения:37
Описание на википедии.
Кликая по полю, можно расставить клетки. Нажав Start/Stop запустить игру и смотреть результат. Еще раз нажав, остановить. Есть еще одна кнопка, которая выставляет паттерн Glider Gun. Нажав ее и затем Start/Stop, можно получить бесконечную жизнь.


import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


public class Life extends Applet implements Runnable{
    private static final byte[][] gliderGunPattern = new byte[][]{
//1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0},
{ 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0},
{ 0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{ 0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
{ 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
{ 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};
    private static final int INITIAL_DELAY = 100;
    private static final int X_CELLS = 50, Y_CELLS = 50;
    private static final int CELL_DIMENSION = 7; // without gap between cells
    private final boolean[][] grid = new boolean[X_CELLS + 4][Y_CELLS + 4];
    private boolean boundsCleared = false;
    private boolean shouldAnimating = false;
    private volatile boolean isAnimating = false;
    private final Component canvas = new Canvas() {
        public void update(Graphics g) {paint(g);}
        public void paint(Graphics g) {
            ++counter;
            g.drawImage(img,
                    (canvas.getSize().width - img.getWidth(null)) / 2,
                    0, this);
        }
    };
    private final Image img = new BufferedImage((X_CELLS)*(CELL_DIMENSION + 1) + 1,
            (Y_CELLS)*(CELL_DIMENSION +1) + 1,
            BufferedImage.TYPE_INT_RGB);
    private final Graphics imgGraphics = img.getGraphics();
    private final Thread logicThread = new Thread(this);
    private long counter = 0;
    private long millis = 0;
    private long delayMillis = INITIAL_DELAY;

    public void run() {
        List newborn = new ArrayList();
        List died = new ArrayList();

        while (true) {
            if (!isAnimating) {
                synchronized (logicThread) {
                    while (!isAnimating) {
                        try { logicThread.wait();
                        } catch (InterruptedException ignored) {}
                    }
                }
            }
            int xLimit = grid.length;
            int yLimit = grid[0].length;
            for (int x = 0; x < xLimit; x++) {
                for (int y = 0; y < yLimit; y++) {

                    int neighbours = 0;
                    if (y - 1 >= 0) {
                        if (grid[x][y - 1]) neighbours++;
                        if (x + 1 < xLimit  && grid[x + 1][y - 1]) neighbours++;
                    }
                    if (x + 1 < xLimit) {
                        if (grid[x + 1][y]) neighbours++;
                        if (y + 1 < yLimit && grid[x + 1][y + 1]) neighbours++;
                    }
                    if (y + 1 < yLimit) {
                        if (grid[x][y + 1]) neighbours++;
                        if (x - 1 >= 0 && grid[x - 1][y + 1]) neighbours++;
                    }
                    if (x - 1 >= 0) {
                        if (grid[x - 1][y]) neighbours++;
                        if (y - 1 >= 0 && grid[x - 1][y - 1]) neighbours++;
                    }

                    if (!grid[x][y] && neighbours == 3) {
                        newborn.add(new Point(x, y));
                    } else if (grid[x][y] && (neighbours < 2
                            || neighbours > 3)) {
                        died.add(new Point(x, y));
                    }

                }
            }

            Iterator it;

            it = newborn.iterator();
            while (it.hasNext()) {
                Point p = (Point)it.next();
                grid[p.x][p.y] = true;
            }
            newborn.clear();

            it = died.iterator();
            while (it.hasNext()) {
                Point p = (Point)it.next();
                grid[p.x][p.y] = false;
            }
            died.clear();

            fillGridImage();
            canvas.repaint();

            if (delayMillis != 0) {
                try {
                    Thread.sleep(delayMillis);
                } catch (InterruptedException e) {
                    return;
                }
            }
        }
    }

    public void start() {
        if (shouldAnimating) {
            isAnimating = true;
            synchronized (logicThread) {
                logicThread.notify();
            }
        }
    }

    public void stop() {
        if (shouldAnimating) {
            isAnimating = false;
        }
    }


    public void init() {
        super.init();

        logicThread.start();

        imgGraphics.setColor(Color.gray);
        imgGraphics.fillRect(0,0,
                img.getWidth(null), img.getHeight(null));
        fillGridImage();

        this.setLayout(new BorderLayout(5, 5));

        final Container north = new Panel();
        final Button startStop = new Button("START/STOP");
        final Button gliderGun = new Button("Gosper glider gun");
        final List recoloredComps = new ArrayList();
        recoloredComps.add(startStop);
        recoloredComps.add(gliderGun);
        final Label fpsLabel = new Label("FPS:   0.00");
        final TextComponent inputField =
                new TextField(String.valueOf(INITIAL_DELAY),2);

        startStop.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (!shouldAnimating) {
                    parseDelayField(inputField);
                    shouldAnimating = true;
                    isAnimating = true;
                    synchronized (logicThread) {
                        logicThread.notify();
                    }
                    counter = 0;
                    millis = System.currentTimeMillis();
                    boundsCleared = false;
                    paintCompsAs(recoloredComps, Color.darkGray, Color.white);
                } else {
                    shouldAnimating = false;
                    isAnimating = false;
                    final double elapsed =
                            ((double)System.currentTimeMillis() - millis) / 1000.0;
                    final String s = "FPS: " + counter / elapsed;
                    fpsLabel.setText(s.length() < 11 ? s : s.substring(0, 11));
                    paintCompsAs(recoloredComps, Color.lightGray, Color.black);
                }
            }
        });
        gliderGun.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                // stop the thread...
                if (shouldAnimating) {
                    shouldAnimating = false;
                    isAnimating = false;
                    paintCompsAs(recoloredComps, Color.lightGray, Color.black);

                }
                clearGridRegion(0, 0 , X_CELLS + 4, Y_CELLS + 4);
                boundsCleared = true;
                int xOffset = (X_CELLS - gliderGunPattern[0].length) / 2;
                int yOffset = (Y_CELLS - gliderGunPattern.length) / 2;
                for (int i = 0; i < gliderGunPattern[0].length; i++) {
                    for (int j = 0; j < gliderGunPattern.length; j++) {
                        grid[i + xOffset][j + yOffset] =
                                gliderGunPattern[j][i] == 1;
                    }
                }
                fillGridImage();
                canvas.repaint();

            }
        });
        inputField.addTextListener(
                new TextListener() {
                    public void textValueChanged(TextEvent e) {
                        TextField txF = (TextField) e.getSource();
                        int pos = txF.getSelectionStart();
                        if (pos == 0) {
                            return; // empty string.
                        }
                        char c = txF.getText().charAt(pos - 1);
                        if (c < '0' || c > '9') {
                            String s = txF.getText();
                            txF.setText(s.substring(0, pos - 1)
                                    + s.substring(pos, s.length()));
                        }
                    }
                }
        );
        north.add(fpsLabel);
        north.add(startStop);
        north.add(gliderGun);
        north.add(new Label("Delay:"));
        north.add(inputField);
        north.add(new Label("ms"));
        north.doLayout();
        this.add(north,BorderLayout.NORTH);
        canvas.addMouseListener(new MouseAdapter() {

            public void mouseClicked(MouseEvent e) {
                if (!shouldAnimating) {
                    if (!boundsCleared) {
                        clearGridRegion(0,0, X_CELLS, 2);
                        clearGridRegion(0 , Y_CELLS - 2, X_CELLS , 2 );
                        clearGridRegion(0, 2, 2, Y_CELLS - 4);
                        clearGridRegion(X_CELLS - 2, 2, 2, Y_CELLS - 4);
                        boundsCleared = true;
                    }
                    int x = e.getX();
                    int y = e.getY();
                    int offset = canvas.getSize().width / 2 -
                            img.getWidth(null) / 2;
                    x -= offset;

                    if (!((x % (CELL_DIMENSION + 1) == 0) ||
                            (y % (CELL_DIMENSION + 1) == 0))) {
                        x /= (CELL_DIMENSION + 1);
                        y /= (CELL_DIMENSION + 1);
                        if (x >= 0 && x < X_CELLS && y >= 0 && y < Y_CELLS) {
                            grid[x + 2][y + 2] = !grid[x + 2][y + 2];
                            changeGridImage(x, y);
                            canvas.repaint();
                        }
                    }
                }
            }
        });
        canvas.setBounds(0,0,
                (CELL_DIMENSION + 1) * X_CELLS + 1,
                (CELL_DIMENSION + 1) * Y_CELLS + 1);
        this.add(canvas, BorderLayout.CENTER);

    }

    private void paintCompsAs(List comps, Color background,
                              Color foreground) {
        Iterator it = comps.iterator();
        while(it.hasNext()) {
            Component c = (Component) it.next();
            c.setBackground(background);
            c.setForeground(foreground);
        }
    }

    private void changeGridImage(int x, int y) {
        imgGraphics.setColor(grid[x+2][y+2] ? Color.blue : Color.white);
        imgGraphics.fillRect((CELL_DIMENSION + 1) * x + 1,
                (CELL_DIMENSION + 1) * y + 1,
                CELL_DIMENSION, CELL_DIMENSION);

    }
    private void fillGridImage() {
        for (int i = 0; i < X_CELLS; i++) {
            for (int j = 0; j < Y_CELLS; j++) {
                changeGridImage(i, j);
            }
        }
    }

    private void clearGridRegion(int x, int y, int width, int height) {
        for (int i = x; i < x + width; i++) {
            for (int j = y; j < y + height; j++) {
                grid[i][j] = false;
            }
        }
    }
    private void parseDelayField(TextComponent tf) {
        String text = tf.getText();
        if ("".equals(text)) {
            tf.setText("0");
            delayMillis = 0;
        } else {
            try {
                delayMillis = Integer.parseInt(text);
            } catch (NumberFormatException e) {
                delayMillis = 10000;
            }
            if (delayMillis > 9999) {
                delayMillis = 9999;
                tf.setText("9999");
            }
        }
    }

    public static void main(String[] args) {
        final Applet a = new Life();
        a.init();
        Window f = new Frame("Life game");
        f.add(a);
        f.pack();
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {System.exit(0);}
            public void windowIconified(WindowEvent e) {a.stop();}
            public void windowDeiconified(WindowEvent e) {a.start();}
        });
        f.setVisible(true);

    }
}



Особенности.
1. Написано под джаву 1.2, для обучающихся по старым книжкам.
2. Написано для Апплета (по той же причине).

Запуск данной программы может производиться тремя методами:
  • обычный запуск как приложения джава - "java Life"
  • запуск в браузере через .html файл:
    <html>
    <title>The Life Game Applet</title>
    <hr>
    <applet code="Life.class" width="500" height="500">
    If your browser was Java-enabled, a "Life" game
    message would appear here.
    </applet>
    <hr>
    </html>

    (Для запуска в браузере могут потребоваться танцы с бубном, ибо в каждом браузере java активируется по разному. К тому же система безопасности Java просто так не позволит запустить не подписанный апплет.)
  • запуск программой appletviewer из комплекта JDK - "appletviewer Life.html"
Изменен:07 янв 2017 23:45
 
 
Сообщения:37
Интересно, что в браузере Java 1.2 достаточно заметно обогнала 1.8 32 бит. Java 1.2 дала 1450 FPS, джава 1.8 32 бит - только 1200 FPS. Хотя обеих покрыла "1.8 64 бит" в запуске standalone приложения - "java Life" - как бык овцу, выдав 2250 FPS!
 
 
Сообщения:37

Слева направо:
  • Измеритель FPS (фреймов в сек)
  • Кнопка пуска-останова
  • Кнопка заполнения поля предопределенным рисунком Gosper Glider Gun
  • Текстовое поле, где можно вводить желаемую задержку между кадрами от 0 до 9999 миллисекунд.
 
Модераторы:Нет
Сейчас эту тему просматривают:Нет