001package ball.game.life;
002/*-
003 * ##########################################################################
004 * Game Applications and Utilities
005 * $Id: Automata.java 6118 2020-06-04 19:31:45Z ball $
006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-game/trunk/src/main/java/ball/game/life/Automata.java $
007 * %%
008 * Copyright (C) 2010 - 2020 Allen D. Ball
009 * %%
010 * Licensed under the Apache License, Version 2.0 (the "License");
011 * you may not use this file except in compliance with the License.
012 * You may obtain a copy of the License at
013 *
014 *      http://www.apache.org/licenses/LICENSE-2.0
015 *
016 * Unless required by applicable law or agreed to in writing, software
017 * distributed under the License is distributed on an "AS IS" BASIS,
018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019 * See the License for the specific language governing permissions and
020 * limitations under the License.
021 * ##########################################################################
022 */
023import java.beans.ConstructorProperties;
024import java.math.BigInteger;
025import lombok.ToString;
026
027/**
028 * Life {@link Automata}.
029 *
030 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
031 * @version $Revision: 6118 $
032 */
033@ToString
034public class Automata {
035    private final int height;
036    private final int width;
037
038    /**
039     * @param   height          The extent of the y-axis.
040     * @param   width           The extent of the x-axis.
041     */
042    @ConstructorProperties({ "height", "width" })
043    public Automata(int height, int width) {
044        if (height > 0) {
045            this.height = height;
046        } else {
047            throw new IllegalArgumentException("height=" + height);
048        }
049
050        if (width > 0) {
051            this.width = width;
052        } else {
053            throw new IllegalArgumentException("width=" + width);
054        }
055    }
056
057    /**
058     * Method to get the height of the board.
059     *
060     * @return  The height.
061     */
062    public int getHeight() { return height; }
063
064    /**
065     * Method to get the width of the board.
066     *
067     * @return  The width.
068     */
069    public int getWidth() { return width; }
070
071    /**
072     * Given the current {@code state}, calculate the next {@code state}.
073     *
074     * @param   current         The current {@code state}.
075     *
076     * @return  The next {@code state}.
077     */
078    public BigInteger next(BigInteger current) {
079        BigInteger next = BigInteger.ZERO;
080
081        for (int y = 0; y < height; y += 1) {
082            for (int x = 0; x < width; x += 1) {
083                int count = 0;
084
085                count += get(current, y - 1, x - 1) ? 1 : 0;
086                count += get(current, y - 1, x)     ? 1 : 0;
087                count += get(current, y - 1, x + 1) ? 1 : 0;
088                count += get(current, y,     x - 1) ? 1 : 0;
089                count += get(current, y,     x + 1) ? 1 : 0;
090                count += get(current, y + 1, x - 1) ? 1 : 0;
091                count += get(current, y + 1, x)     ? 1 : 0;
092                count += get(current, y + 1, x + 1) ? 1 : 0;
093
094                if (get(current, y, x)) {
095                    switch (count) {
096                    case 2:
097                    case 3:
098                        next = next.setBit(y * width + x);
099                        break;
100
101                    default:
102                        next = next.clearBit(y * width + x);
103                        break;
104                    }
105                } else {
106                    switch (count) {
107                    case 3:
108                        next = next.setBit(y * width + x);
109                        break;
110
111                    default:
112                        next = next.clearBit(y * width + x);
113                        break;
114                    }
115                }
116            }
117        }
118
119        return next;
120    }
121
122    /**
123     * Method to get the value of a cell at a specified {@code (y, x)}
124     * coordinate fron the argument {@code state}.
125     *
126     * @param   state           The {@link Automata} {@code state}.
127     * @param   y               The {@code y} coordinate.
128     * @param   x               The {@code x} coordinate.
129     *
130     * @return  {@code true} if the {@code (y, x)} coordinate is valid and
131     *          if the specified cell is "alive;" {@code false} otherwise.
132     */
133    public boolean get(BigInteger state, int y, int x) {
134        return ((0 <= y && y < height && 0 <= x && x < width)
135                && state.testBit(y * width + x));
136    }
137}