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