package com.freetymekiyan.algorithms.level.medium;

/**
 * According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised
 * by the British mathematician John Horton Conway in 1970."
 * <p>
 * Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its
 * eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia
 * article):
 * <p>
 * Any live cell with fewer than two live neighbors dies, as if caused by under-population.
 * Any live cell with two or three live neighbors lives on to the next generation.
 * Any live cell with more than three live neighbors dies, as if by over-population..
 * Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
 * Write a function to compute the next state (after one update) of the board given its current state.
 * <p>
 * Follow up:
 * Could you solve it in-place? Remember that the board needs to be updated at the same time: You cannot update some
 * cells first and then use their updated values to update other cells.
 * In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause
 * problems when the active area encroaches the border of the array. How would you address these problems?
 * <p>
 * Tags: Array
 * Similar Problems: (M) Set Matrix Zeroes
 */
public class GameOfLife {

    /**
     * Bit-manipulation.
     * Use 2 bit for each cell to represent both current and next states.
     * 00, 01, 10, 11.
     * Then scan the matrix again to generate result.
     */
    public void gameOfLife(int[][] board) {
        int m = board.length;
        int n = board[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int lives = -board[i][j]; // If the current cell is live, it will be deducted from result
                for (int k = Math.max(i - 1, 0); k < Math.min(i + 2, m); k++) {
                    for (int l = Math.max(j - 1, 0); l < Math.min(j + 2, n); l++) {
                        lives += board[k][l] & 1;
                    }
                }
                if ((lives | board[i][j]) == 3) { // lives = 2 or 3, board[i][j] = 1, or lives = 3, board[i][j] = 0
                    board[i][j] |= 2;
                }
            }
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                board[i][j] >>= 1;
            }
        }
    }
}
add Game of Life