class ArgumentsInterpreterTest
extends GroovyTestCase
{
def interpreter =
new ArgumentsInterpreter
()
void testInterpretsArgumentWithoutValue
() {
interpreter
<< new ExpectedArgument
(shortcut:
'-h', argument:
'printHelp')
def arguments = interpreter.
interpret(['-h'])
assertNotNull
(arguments
['printHelp'])
}
void testInterpretsNoArguments
() {
def arguments = interpreter.
interpret([])
assertTrue
(arguments.
isEmpty())
}
void testInterpretsArgumentWithValue
() {
interpreter
<< new ExpectedArgument
(shortcut:
'-t', argument:
'tickInterval', defaultValue:
1)
def arguments = interpreter.
interpret(['-t',
'2'])
assertEquals
('2', arguments.
tickInterval)
}
void testAcceptsArgumentWithoutDefaultValue
() {
interpreter
<< new ExpectedArgument
(shortcut:
'-h', argument:
'printHelp')
def arguments = interpreter.
interpret([])
assertNull
(arguments
['printHelp'])
}
void testAcceptsArgumentWithDefaultValue
() {
interpreter
<< new ExpectedArgument
(shortcut:
'-t', argument:
'tickInterval', defaultValue:
1)
def arguments = interpreter.
interpret([])
assertEquals
('1', arguments.
tickInterval)
}
void testInterpretsTwoArgumentsWithDefaultValues
() {
interpreter
<<
new ExpectedArgument
(shortcut:
'-a', argument:
'b', defaultValue:
1) <<
new ExpectedArgument
(shortcut:
'-c', argument:
'd', defaultValue:
2)
def arguments = interpreter.
interpret(['-a',
'3',
'-c',
'4'])
assertEquals
('3', arguments.
b)
assertEquals
('4', arguments.
d)
}
void testInterpretsTwoArgumentsOneWithAndOneWithoutDefaultValue
() {
interpreter
<<
new ExpectedArgument
(shortcut:
'-a', argument:
'b') <<
new ExpectedArgument
(shortcut:
'-c', argument:
'd', defaultValue:
1)
def arguments = interpreter.
interpret(['-a',
'-c',
'2'])
assertNotNull
(arguments.
b)
assertEquals
('2', arguments.
d)
}
void testPrintsSummary
() {
interpreter
<<
new ExpectedArgument
(shortcut:
'-d', argument:
'e', defaultValue:
1, description:
'f') <<
new ExpectedArgument
(shortcut:
'-a', argument:
'b', description:
'c') <<
new ExpectedArgument
(shortcut:
'-f', argument:
'long', defaultValue:
2, description:
'g')
assertEquals
('-a c\n' +
'-d <e> f;\n default value is 1\n' +
'-f <long> g;\n default value is 2\n', interpreter.
summary)
}
}
class ConwaysGameOfLifeTest
extends GroovyTestCase
{
static def BLINKER_HORIZONTAL =
"[' ', 'XXX', ' ']"
static def BLINKER_VERTICAL =
"[' X ', ' X ', ' X ']"
void testBlinker
() {
def universe =
new ConwaysGameOfLife
().
createUniverse(new GroovyShell
().
evaluate(BLINKER_HORIZONTAL
))
assertEquals
(BLINKER_HORIZONTAL, universe.
toString())
assertEquals
(BLINKER_VERTICAL, universe.
next().
toString())
assertEquals
(BLINKER_HORIZONTAL, universe.
next().
toString())
}
static def TOAD_FIRST_TICK =
"[' ', ' XXX ', ' XXX ', ' ']"
static def TOAD_SECOND_TICK =
"[' X ', ' X X ', ' X X ', ' X ']"
void testToad
() {
def universe =
new ConwaysGameOfLife
().
createUniverse(new GroovyShell
().
evaluate(TOAD_FIRST_TICK
))
assertEquals
(TOAD_FIRST_TICK, universe.
toString())
assertEquals
(TOAD_SECOND_TICK, universe.
next().
toString())
assertEquals
(TOAD_FIRST_TICK, universe.
next().
toString())
}
}
class RuleBuilderTest
extends GroovyTestCase
{
void testBuildsRules
() {
def expectedFrom =
'a'
def expectedWithLivingNeighbours =
'b'
def expectedTo =
'c'
def ruler =
new RuleBuilder
().
rules {
rule
(from:expectedFrom, withLivingNeighbours:expectedWithLivingNeighbours, to:expectedTo
)
}
assertEquals
(1, ruler.
size())
def rule = ruler
[0]
assertEquals
(expectedFrom, rule.
from)
assertEquals
(expectedWithLivingNeighbours, rule.
withLivingNeighbours)
assertEquals
(expectedTo, rule.
to)
}
}
import org.easymock.EasyMock
class RulerTest
extends GroovyTestCase
{
def ruler =
new Ruler
()
void testAppliesRuleOnCell
() {
def ruleMock = EasyMock.
createMock(IRule.
class)
def cellDummy =
new Expando
()
def universeExpando =
new Expando
()
universeExpando.
eachCellWithXY =
{ closure -> closure.
call(cellDummy,
0,
0) }
universeExpando.
switchToNextTick =
{ /*dummy behaviour */ }
def expectedNumberRulesAppliedTo =
2
ruleMock.
applyTo(cellDummy
)
EasyMock.
expectLastCall().
times(expectedNumberRulesAppliedTo
)
EasyMock.
replay(ruleMock
)
expectedNumberRulesAppliedTo.
times { ruler
<< ruleMock
}
ruler.
applyRulesTo(universeExpando
)
EasyMock.
verify(ruleMock
)
}
void testSwitchesUniverseToNextTickAfterApplyingRules
() {
def universeMock = EasyMock.
createMock(IUniverse.
class)
universeMock.
switchToNextTick()
EasyMock.
replay(universeMock
)
ruler.
applyRulesTo(universeMock
)
EasyMock.
verify(universeMock
)
}
}
class RuleTest
extends GroovyTestCase
{
static def LIVE_CELL =
new Cell
(Cell.
LIVE)
void testDoesntApplyItselfToCellWithDifferentStartStateThenRulesFromState
() {
def rule =
new Rule
(from:Cell.
LIVE)
def cell =
new Cell
(Cell.
DEAD)
rule.
applyTo(cell
)
assertNull cell.
nextState
}
void testAppliesItselfToCellWithSameStartStateThenRulesFromState
() {
def rule =
new Rule
(from:Cell.
LIVE, to:Cell.
DEAD)
def cell =
new Cell
(Cell.
LIVE)
rule.
applyTo(cell
)
assertEquals
(Cell.
DEAD, cell.
nextState)
}
void testDoesntApplyItselfToCellWithoutEnoughNeighbours
() {
def rule =
new Rule
(from:Cell.
LIVE, withLivingNeighbours:
1..
2, to:Cell.
DEAD)
def cell =
new Cell
(Cell.
LIVE)
rule.
applyTo(cell
)
assertNull cell.
nextState
cell.
neighbours =
[north:LIVE_CELL, west:LIVE_CELL, south:LIVE_CELL
]
rule.
applyTo(cell
)
assertNull cell.
nextState
}
void testAppliesItselfToCellWithEnoughNeighbours
() {
def rule =
new Rule
(from:Cell.
LIVE, withLivingNeighbours:
1..
2, to:Cell.
DEAD)
def cellWithOneNeighbour =
new Cell
(Cell.
LIVE)
cellWithOneNeighbour.
neighbours.
north = LIVE_CELL
rule.
applyTo(cellWithOneNeighbour
)
assertEquals
(Cell.
DEAD, cellWithOneNeighbour.
nextState)
def cellWithTwoNeighbours =
new Cell
(Cell.
LIVE)
cellWithTwoNeighbours.
neighbours.
south = LIVE_CELL
cellWithTwoNeighbours.
neighbours.
north = LIVE_CELL
rule.
applyTo(cellWithTwoNeighbours
)
assertEquals
(Cell.
DEAD, cellWithTwoNeighbours.
nextState)
}
void testDoesntApplyAnyRuleOnAlreadyChangedState
() {
def rule =
new Rule
(from:Cell.
LIVE, to:Cell.
DEAD)
def changedCell =
new Cell
(Cell.
LIVE)
changedCell.
nextState = Cell.
DEAD
}
}
class UniverseTest
extends GroovyTestCase
{
void testCreatesItselfByWidthAndHeightWithAllDeadCells
() {
def universe =
new Universe
(3,
3, Cell.
DEAD)
assertSame Cell.
DEAD, universe
[0][0].
state
assertSame Cell.
DEAD, universe
[2][2].
state
assertNull universe
[3][2]
assertNull universe
[2][3]
assertEquals
9, universe.
size()
}
void testCreatesItselfByPatternWithAllDeadCells
() {
def universe =
new Universe
([' ',
' '])
assertEquals
(new Universe
(3,
2, Cell.
DEAD), universe
)
assertEquals
(3, universe.
width)
assertEquals
(2, universe.
height)
}
void testCreatesItselfByPatternWithLiveCellsInTheCornersAndInTheMiddle
() {
def universe =
new Universe
(3,
3, Cell.
DEAD)
universe
[0][0].
alive()
universe
[2][0].
alive()
universe
[1][1].
alive()
universe
[0][2].
alive()
universe
[2][2].
alive()
assertEquals
(universe,
new Universe
(['X X',
' X ',
'X X']))
}
void testCountsNeighboursByState
() {
def universe =
new Universe
(3,
3, Cell.
LIVE)
assertEquals
3, universe
[0][0].
countLiveNeighbours()
assertEquals
5, universe
[1][0].
countLiveNeighbours()
assertEquals
8, universe
[1][1].
countLiveNeighbours()
}
void testIteratesOverEachCellWithxAndYCoords
() {
def universe =
new Universe
(2,
1, Cell.
LIVE)
def counter =
0;
universe.
eachCellWithXY{ cell, x, y -> cell.
state = Cell.
DEAD; counter++
}
assertEquals
(2, counter
)
assertEquals
(Cell.
DEAD, universe
[0][0].
state)
assertEquals
(Cell.
DEAD, universe
[0][1].
state)
}
void testSwitchesToNextTick
() {
def universe =
new Universe
(2,
1, Cell.
LIVE)
universe
[0][0].
nextState = Cell.
DEAD
universe.
switchToNextTick()
assertEquals
(Cell.
DEAD, universe
[0][0].
state)
assertEquals
(Cell.
LIVE, universe
[0][1].
state)
assertNull
(universe
[0][0].
nextState)
assertNull
(universe
[0][1].
nextState)
}
}
class ArgumentsInterpreter
{
def expectedArguments =
[:
]
def interpret
(args
) {
def arguments = createDefaultArguments
()
args.
eachWithIndex{ arg, index ->
if(arg
in expectedArguments.
keySet()) {
def expectedArgument = expectedArguments
[arg
]
if(!expectedArgument.
defaultValue) arguments
[expectedArgument.
argument] =
true
else arguments
[expectedArgument.
argument] = args
[index +
1]
}
}
return arguments
}
def leftShift
(expectedArgument
) {
expectedArguments
[expectedArgument.
shortcut] = expectedArgument
this
}
private createDefaultArguments
() {
def arguments =
[:
]
expectedArguments.
values().
each{ expectedArgument ->
if(expectedArgument.
defaultValue)
arguments
[expectedArgument.
argument] = expectedArgument.
defaultValue as String
}
arguments
}
def getSummary
() {
def arguments = expectedArguments.
values()
arguments = arguments.
sort{ it.
shortcut }
def longestArgument = arguments.
findAll{ it.
defaultValue }.
argument.
sort{ it.
size() }[-1].
size()
def summary =
''
arguments.
each{ argument ->
summary
<<= argument.
shortcut
def argumentPart =
' '
if(argument.
defaultValue) {
argumentPart =
" <${argument.argument}> "
}
summary
<<= argumentPart.
padRight(longestArgument +
4,
' ')
summary
<<= argument.
description
if(argument.
defaultValue) {
summary
<<=
';\n'
summary
<<=
' ' *
(longestArgument +
6)
summary
<<=
"default value is ${argument.defaultValue}"
}
summary
<<=
'\n'
}
summary.
toString()
}
}
class Cell
{
static def DEAD = CellState.
DEAD
static def LIVE = CellState.
LIVE
def state = DEAD
def nextState
def neighbours =
[:
]
Cell
(CellState state
) {
this.
state = state
}
Cell
(char statePattern
) {
state = CellState.
get(statePattern
)
}
def countLiveNeighbours
() {
neighbours.
findAll{ entry -> LIVE == entry.
value?.
state}.
size()
}
def alive
() { state = LIVE
}
String toString
() {
state.
toString()
}
}
class CellState
{
static def LIVE =
new CellState
(state:
'X')
static def DEAD =
new CellState
(state:
' ')
private state
static def get(statePattern
) {
if(statePattern == LIVE.
state)
return LIVE
DEAD
}
String toString
() { state
}
}
class ConwaysGameOfLife
{
private static def RULER =
new RuleBuilder
().
rules {
rule
(from:Cell.
LIVE, withLivingNeighbours:
0..
1, to:Cell.
DEAD)
rule
(from:Cell.
LIVE, withLivingNeighbours:
3..
9, to:Cell.
DEAD)
rule
(from:Cell.
LIVE, withLivingNeighbours:
2..
3, to:Cell.
LIVE)
rule
(from:Cell.
DEAD, withLivingNeighbours:
3, to:Cell.
LIVE)
}
def universe
def createUniverse
(pattern
) {
universe =
new Universe
(pattern
)
this
}
def next
() {
RULER.
applyRulesTo(universe
)
this
}
def eachCellWithXY
(def closure
) { universe.
eachCellWithXY(closure
) }
def getHeight
() { universe.
height }
def getWidth
() { universe.
width }
String toString
() { universe.
toString() }
}
class ExpectedArgument
{
def shortcut
def argument
def defaultValue
def description
String toString
() { shortcut
}
}
interface IRule
{
void applyTo
(cell
)
}
interface IUniverse
{
void switchToNextTick
()
}
import groovy.swing.SwingBuilder
import javax.swing.JComponent
import java.awt.Color
import javax.swing.WindowConstants
class Main
{
static def TOAD =
"[' ',' XXX ',' XXX ',' ']"
static def BLINKER =
"[' ','XXX',' ']"
static def GUN =
"[' X '," +
"' X X '," +
"' XX XX XX'," +
"' X X XX XX'," +
"'XX X X XX '," +
"'XX X X XX X X '," +
"' X X X '," +
"' X X '," +
"' XX '," +
("' '," *
18) +
"' ']"
static void main
(args
) {
def interpreter =
new ArgumentsInterpreter
() <<
new ExpectedArgument
(shortcut:
'-h', argument:
'printHelp',
description:
'prints this help text') <<
new ExpectedArgument
(shortcut:
'-t', argument:
'tickInterval', defaultValue:
200,
description:
'sets the tick interval in milliseconds') <<
new ExpectedArgument
(shortcut:
'-p', argument:
'pattern', defaultValue:TOAD,
description:
'sets the inital pattern (try TOAD, BLINKER or GUN)') <<
new ExpectedArgument
(shortcut:
'-b', argument:
'blockSize', defaultValue:
10,
description:
'sets the length of a block in pixel') <<
new ExpectedArgument
(shortcut:
'-v', argument:
'view', defaultValue:
'swing',
description:
'sets the view to <swing> or <console>')
def arguments = interpreter.
interpret(args
)
if(arguments.
printHelp) {
printHelp
(interpreter
)
return
}
def pattern = calculatePattern
(arguments
)
def universe =
new ConwaysGameOfLife
().
createUniverse(pattern
)
def view =
new GroovyShell
(new Binding(Main:Main.
class)).
evaluate("Main.&${arguments.view}")
view
(universe, arguments
)
}
static calculatePattern
(arguments
) {
if(arguments.
pattern[0] !=
'[') arguments.
pattern = Main.
"${arguments.pattern}"
return new GroovyShell
(new Binding(Main:Main.
class)).
evaluate(arguments.
pattern)
}
static void printHelp
(interpreter
) {
println 'Usage: java -jar conwaysgame.jar [arguments]\n' + interpreter.
summary
}
static void console
(universe, arguments
) {
while(true) {
println universe.
toString()[2..
-3].
split('\', \'').
join('\n')
universe = universe.
next()
Thread.
sleep(arguments.
tickInterval.
toInteger())
}
}
static void swing
(universe, arguments
) {
def swing =
new SwingBuilder
()
def blockSize = arguments.
blockSize.
toInteger()
def size =
[universe.
width * blockSize, universe.
height * blockSize
]
def painterPanel =
new PainterPanel
(blockSize:blockSize, universe:universe,
tickInterval:arguments.
tickInterval)
def frame = swing.
frame (title:
'Conway\'s Game of Life in Groovy',
size: