Created
December 9, 2012 17:27
-
-
Save satokitty/4246197 to your computer and use it in GitHub Desktop.
Coderetreat2012 Lifegame実装例(Java)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package coderetreat.lifegame; | |
import static org.hamcrest.CoreMatchers.is; | |
import static org.hamcrest.CoreMatchers.not; | |
import static org.junit.Assert.assertThat; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.HashSet; | |
import java.util.Set; | |
import org.junit.Test; | |
import org.junit.experimental.runners.Enclosed; | |
import org.junit.runner.RunWith; | |
/** | |
* 誕生:指定セル 死亡、隣接セル 生存数が3であった場合、次の世代では生存になる | |
* 維持:指定セル 生存、隣接セル 2 <= 生存数 <= 3の場合、次の世代では生存 | |
* 過疎:指定セル 生存、隣接セル 生存数 < 2の場合、次の世代では死亡 | |
* 過密:指定セル 生存、隣接セル 3 < 生存数の場合、次の世代では死亡 | |
*/ | |
enum Cell { | |
LIVE { | |
@Override | |
public boolean isAliveNextGen(int aliveCount) { | |
return 1 < aliveCount && aliveCount < 4; | |
} | |
}, | |
DEAD { | |
@Override | |
public boolean isAliveNextGen(int aliveCount) { | |
return aliveCount == 3; | |
} | |
}; | |
abstract public boolean isAliveNextGen(int aliveCount); | |
} | |
class Point { | |
private final int x; | |
private final int y; | |
public Point(int x, int y) { | |
this.x = x; | |
this.y = y; | |
} | |
public Point add(int x, int y) { | |
return new Point(this.x + x, this.y + y); | |
} | |
@Override | |
public int hashCode() { | |
final int prime = 31; | |
int result = 1; | |
result = prime * result + x; | |
result = prime * result + y; | |
return result; | |
} | |
@Override | |
public boolean equals(Object obj) { | |
if (this == obj) return true; | |
if (obj == null) return false; | |
if (getClass() != obj.getClass()) return false; | |
Point other = (Point) obj; | |
if (x != other.x) return false; | |
if (y != other.y) return false; | |
return true; | |
} | |
} | |
class Size { | |
public final int x; | |
public final int y; | |
public Size(int x, int y) { | |
this.x = x; | |
this.y = y; | |
} | |
@Override | |
public int hashCode() { | |
final int prime = 31; | |
int result = 1; | |
result = prime * result + x; | |
result = prime * result + y; | |
return result; | |
} | |
@Override | |
public boolean equals(Object obj) { | |
if (this == obj) return true; | |
if (obj == null) return false; | |
if (getClass() != obj.getClass()) return false; | |
Size other = (Size) obj; | |
if (x != other.x) return false; | |
if (y != other.y) return false; | |
return true; | |
} | |
} | |
class Board { | |
private final Set<Point> alivePoints; | |
private final Size size; | |
private final Set<Point> allPoints; | |
public Board(Size size, Set<Point> alivePoints) { | |
this.size = size; | |
this.alivePoints = Collections.unmodifiableSet(alivePoints); | |
Set<Point> allPoints = new HashSet<Point>(); | |
for (int i = 0; i < size.y; i++) { | |
for (int j = 0; j < size.x; j++) { | |
allPoints.add(new Point(j, i)); | |
} | |
} | |
this.allPoints = Collections.unmodifiableSet(allPoints); | |
} | |
public Board next() { | |
return new Board(size, nextAlivePoints()); | |
} | |
private Set<Point> nextAlivePoints() { | |
Set<Point> next = new HashSet<Point>(); | |
for (Point p : allPoints) { | |
if (cell(p).isAliveNextGen(aliveNeighbors(p).size())) { | |
next.add(p); | |
} | |
} | |
return next; | |
} | |
private Cell cell(Point point) { | |
return alivePoints.contains(point) == true ? Cell.LIVE : Cell.DEAD; | |
} | |
Set<Point> aliveNeighbors(Point point) { | |
Set<Point> neighbors = getNeighborPoints(point); | |
neighbors.retainAll(alivePoints); | |
return neighbors; | |
} | |
Set<Point> getNeighborPoints(Point point) { | |
Set<Point> neighbors = new HashSet<Point>(); | |
for (int i = -1; i < 2; i++) { | |
for (int j = -1; j < 2; j++) { | |
if (i == 0 && j == 0) continue; | |
neighbors.add(point.add(i, j)); | |
} | |
} | |
return neighbors; | |
} | |
public String toString() { | |
StringBuilder str = new StringBuilder(); | |
for (int i = 0; i < size.y; i++) { | |
for (int j = 0; j < size.x; j++) { | |
str.append(cell(new Point(j, i)) == Cell.LIVE ? "O" : "X"); | |
} | |
str.append("\n"); | |
} | |
return str.toString(); | |
} | |
@Override | |
public int hashCode() { | |
final int prime = 31; | |
int result = 1; | |
result = prime * result + ((alivePoints == null) ? 0 : alivePoints.hashCode()); | |
result = prime * result + ((size == null) ? 0 : size.hashCode()); | |
return result; | |
} | |
@Override | |
public boolean equals(Object obj) { | |
if (this == obj) return true; | |
if (obj == null) return false; | |
if (getClass() != obj.getClass()) return false; | |
Board other = (Board) obj; | |
if (alivePoints == null) { | |
if (other.alivePoints != null) return false; | |
} else if (!alivePoints.equals(other.alivePoints)) return false; | |
if (size == null) { | |
if (other.size != null) return false; | |
} else if (!size.equals(other.size)) return false; | |
return true; | |
} | |
public static Board buildBoard(Size size, Point... alivePoints) { | |
return new Board(size, new HashSet<Point>(Arrays.asList(alivePoints))); | |
} | |
} | |
@RunWith(Enclosed.class) | |
public class LifeGameTest { | |
public static class CellTest { | |
@Test | |
public void LIVEの場合近傍セルの生存数が1以下の場合次世代では死亡() throws Exception { | |
assertThat(Cell.LIVE.isAliveNextGen(1), is(false)); | |
} | |
@Test | |
public void LIVEの場合近傍セルの生存数が4以上の場合次世代では死亡() throws Exception { | |
assertThat(Cell.LIVE.isAliveNextGen(4), is(false)); | |
} | |
@Test | |
public void LIVEの場合近傍セルの生存数が2または3の場合次世代では生存() throws Exception { | |
assertThat(Cell.LIVE.isAliveNextGen(2), is(true)); | |
assertThat(Cell.LIVE.isAliveNextGen(3), is(true)); | |
} | |
@Test | |
public void DEADの場合近傍セルの生存数が3の場合次世代では生存() throws Exception { | |
assertThat(Cell.DEAD.isAliveNextGen(3), is(true)); | |
} | |
@Test | |
public void DEADの場合近傍セルの生存数が3以外の場合次世代では死亡() throws Exception { | |
assertThat(Cell.DEAD.isAliveNextGen(2), is(false)); | |
assertThat(Cell.DEAD.isAliveNextGen(4), is(false)); | |
} | |
} | |
public static class PointTest { | |
@Test | |
public void Point同士で比較できる_同値の場合true() throws Exception { | |
Point sut1 = new Point(0, 1); | |
Point sut2 = new Point(0, 1); | |
assertThat(sut1, is(sut2)); | |
} | |
@Test | |
public void Point同士で比較できる_同値でない場合false() throws Exception { | |
Point sut1 = new Point(0, 1); | |
Point sut2 = new Point(1, 0); | |
assertThat(sut1, is(not(sut2))); | |
} | |
@Test | |
public void addメソッドでオフセットを加算したPointオブジェクトを取得できる() throws Exception { | |
Point sut = new Point(2, 3); | |
Point actual = sut.add(1, 2); | |
Point expected = new Point(3, 5); | |
assertThat(actual, is(expected)); | |
} | |
@Test | |
public void addメソッドに負の値を指定することもできる() throws Exception { | |
Point sut = new Point(2, 3); | |
Point actual = sut.add(-2, -3); | |
assertThat(actual, is(new Point(0, 0))); | |
} | |
} | |
public static class BoardTest { | |
@Test | |
public void buildBoardメソッドでボードを生成できる() throws Exception { | |
// given | |
Size size = new Size(3, 3); | |
// when | |
Board board = Board.buildBoard(size, new Point(0, 0), new Point(0, 1), new Point(1, 0)); | |
// then | |
assertThat(board.toString(), is("OOX\nOXX\nXXX\n")); | |
} | |
@Test | |
public void 与えたサイズと生存セルの座標でボードを作る() throws Exception { | |
// given | |
Set<Point> alivePoints = new HashSet<Point>(Arrays.asList( | |
new Point(0, 0), new Point(0, 1), new Point(1, 0) | |
)); | |
Size size = new Size(3, 3); | |
// when | |
Board board = new Board(size, alivePoints); | |
// then | |
assertThat(board.toString(), is("OOX\nOXX\nXXX\n")); | |
} | |
@Test | |
public void equalsメソッドでBoardインスタンス同士を値で比較できる() throws Exception { | |
Board sut1 = Board.buildBoard(new Size(3, 3), new Point(0, 0), new Point(0, 1), new Point(1, 0)); | |
Board sut2 = Board.buildBoard(new Size(3, 3), new Point(0, 0), new Point(0, 1), new Point(1, 0)); | |
assertThat(sut1, is(sut2)); | |
} | |
@Test | |
public void nextメソッドで一世代後のボードが取得できる() throws Exception { | |
// given | |
/* | |
* oox | |
* oxx | |
* xxx | |
*/ | |
Board sut = Board.buildBoard(new Size(3, 3), new Point(0, 0), new Point(0, 1), new Point(1, 0)); | |
// when | |
Board actual = sut.next(); | |
// then | |
/* | |
* oox | |
* oox | |
* xxx | |
*/ | |
Board expected = Board.buildBoard(new Size(3, 3), | |
new Point(0, 0), new Point(0, 1), new Point(1, 0), new Point(1, 1)); | |
assertThat(actual, is(expected)); | |
} | |
@Test | |
public void nextメソッドで一世代後のボードが取得できる_2() throws Exception { | |
// given | |
/* | |
* oxx | |
* oxx | |
* oxx | |
*/ | |
Board sut = Board.buildBoard(new Size(3, 3), new Point(0, 0), new Point(0, 1), new Point(0, 2)); | |
// when | |
Board actual = sut.next(); | |
// then | |
/* | |
* xxx | |
* oox | |
* xxx | |
*/ | |
Board expected = Board.buildBoard(new Size(3, 3), | |
new Point(0, 1), new Point(1, 1)); | |
assertThat(actual, is(expected)); | |
} | |
@Test | |
public void aliveNeighborsメソッドで指定したセルを含まない生存している近傍セルを取得() throws Exception { | |
// given | |
/* | |
* OOXX | |
* OXOX | |
* XXXX | |
* XXXX | |
*/ | |
Board sut = Board.buildBoard(new Size(4, 4), | |
new Point(0, 0), new Point(0, 1), | |
new Point(1, 0), new Point(1, 2) | |
); | |
// when | |
Set<Point> actual = sut.aliveNeighbors(new Point(1, 2)); | |
// then | |
Set<Point> expected = new HashSet<Point>(Arrays.asList( | |
new Point(0, 1) | |
)); | |
assertThat(actual, is(expected)); | |
} | |
@Test | |
public void aliveNeighborsメソッドで隅を指定した場合_範囲外のセルは無いものとして扱われる() throws Exception { | |
// given | |
/* | |
* OOXX | |
* OXOX | |
* XXXX | |
* XXXX | |
*/ | |
Board sut = Board.buildBoard(new Size(4, 4), | |
new Point(0, 0), new Point(0, 1), | |
new Point(1, 0), new Point(1, 2) | |
); | |
// when | |
Set<Point> actual = sut.aliveNeighbors(new Point(0, 0)); | |
// then | |
Set<Point> expected = new HashSet<Point>(Arrays.asList( | |
new Point(0, 1), | |
new Point(1, 0) | |
)); | |
assertThat(actual, is(expected)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment