/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
* Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
* This file is available and licensed under the following license:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Oracle Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package gameoflife;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.Group;
import javafx.scene.paint.*;
import javafx.scene.Scene;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextOrigin;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.scene.input.MouseEvent;
def screenWidth: Integer = 240;
def screenHeight: Integer = 320;
def interval = .3s;
def diameter = 21;
def gap = 2;
def unitSize = diameter + gap;
def smallFont = Font { size: 15 }
def liveColor = LinearGradient {
startX: 0.0,
startY: 0.0,
endX: 0.0,
endY: 1.0
proportional: true
stops: [
Stop {
offset: 0.0
color: Color.LIGHTGREEN
},
Stop {
offset: 0.3
color: Color.DARKGREEN
},
Stop {
offset: 1.0
color: Color.LIGHTGREEN
}
]
};
def backgroundColor = Color.BLACK;
def evenColor = LinearGradient {
startX: 0.0,
startY: 0.0,
endX: 0.0,
endY: 1.0
proportional: true
stops: [
Stop {
offset: 0.0
color: Color.RED
},
Stop {
offset: 0.3
color: Color.DARKRED
},
Stop {
offset: 1.0
color: Color.RED
}
]
};
def oddColor = evenColor;
def resetSize = Text {
content: "Reset"
font: smallFont
};
def buttonHeight:Integer = (resetSize.layoutBounds.height + 8) as Integer;
def buttonWidth:Integer = (resetSize.layoutBounds.width + 10) as Integer;
def useableHeight = screenHeight - buttonHeight - gap;
def gameSize =
if (useableHeight > screenWidth) screenWidth else useableHeight;
def buttonY = screenHeight - buttonHeight - gap;
def nCells: Integer = (gameSize - gap) / unitSize;
def lastRectIndex = nCells * nCells - 1;
class CircleGroup extends Group {
var myColor: Paint;
override var content =
for (i in [0..lastRectIndex]) {
Circle {
translateX: gap + unitSize * (i mod nCells)
translateY: gap + unitSize * ((
i / nCells) as Integer)
centerX: 15, centerY: 15
radius: diameter/2
fill:LinearGradient {
startX: 0.0,
startY: 0.0,
endX: 0.0,
endY: 1.0
proportional: true
stops: [
Stop {
offset: 0.0
color: Color.RED
},
Stop {
offset: 0.3
color: Color.DARKRED
},
Stop {
offset: 1.0
color: Color.RED
}
]
}
}
}
override var onMousePressed = function(event: MouseEvent) {
var xx = ((event.sceneX - gap) / (unitSize)) as Integer;
var yy = ((event.sceneY - gap) / (unitSize)) as Integer;
var index = yy * nCells + xx;
def theRect = this.content[index] as Circle;
if (theRect.fill == liveColor) {
theRect.fill = this.myColor;
} else {
theRect.fill = liveColor;
}
}
}
def cEven = CircleGroup { myColor: evenColor };
def cOdd = CircleGroup { myColor: oddColor };
var rectsCurr = cEven;
var count = 0;
function getLiveNeighborsCount(theCircleGroup: CircleGroup, ii: Integer): Integer {
var retVal: Integer = 0;
def prevRow = ii - nCells;
def nextRow = ii + nCells;
def left = ii mod nCells;
def right = (ii + 1) mod nCells;
def theSeq = theCircleGroup.content;
if (prevRow >= 0) {
if (left != 0) {
if ((theSeq[
prevRow - 1] as Circle).fill == liveColor) retVal++;
}
if ((theSeq[prevRow] as Circle).fill == liveColor) retVal++;
if (right != 0) {
if ((theSeq[
prevRow + 1] as Circle).fill == liveColor) retVal++;
}
}
if (left != 0) {
if ((theSeq[
ii - 1] as Circle).fill == liveColor) retVal++;
}
if (right != 0) {
if ((theSeq[
ii + 1] as Circle).fill == liveColor) retVal++;
}
if (nextRow < lastRectIndex) {
if (left != 0) {
if ((theSeq[
nextRow - 1] as Circle).fill == liveColor) retVal++;
}
if ((theSeq[nextRow] as Circle).fill == liveColor) retVal++;
if (right != 0) {
if ((theSeq[
nextRow + 1] as Circle).fill == liveColor) retVal++;
}
}
return retVal;
}
function reset(theRect: CircleGroup) {
for (ii in [0..lastRectIndex]) {
(
theRect.content[ii] as Circle).fill = theRect.myColor;
}
}
var tt: Timeline = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames: [
KeyFrame {
canSkip: true
time: interval
action: function() {
var allDead = true;
count++;
var nextRect: CircleGroup;
var currRect: CircleGroup;
if (count mod 2 == 1) {
nextRect = cOdd;
currRect = cEven;
} else {
nextRect = cEven;
currRect = cOdd;
}
for (ii in [0..lastRectIndex]) {
def nLive = getLiveNeighborsCount(currRect, ii);
def curr = currRect.content[ii] as Circle;
def next = nextRect.content[ii] as Circle;
if (curr.fill == liveColor) {
if (nLive < 2 or nLive > 3) {
next.fill = nextRect.myColor;
} else {
next.fill = liveColor;
allDead = false;
}
continue;
}
if (nLive == 3) {
next.fill = liveColor;
allDead = false;
} else {
next.fill = nextRect.myColor
}
}
rectsCurr = nextRect;
if (allDead) {
tt.stop();
}
}
}
]
}
var start = Group {
content: [
Rectangle {
x: 2
y: 290
width: 75
height: 25
opacity: 0.6
arcHeight:5
arcWidth:5
fill: LinearGradient {
startX: 0.0
startY: 0.0
endX: 0.0
endY: 1.0
proportional: true
stops: [
Stop {
offset: 0.0
color: Color.LIME
},
Stop {
offset: 0.5
color: Color.DARKGREEN
},
Stop {
offset: 1.0
color: Color.LIME
}
]
}
onMousePressed: function(e) {
tt.play();
}
},
Text {
fill: Color.WHITE
x: 27
y: 307
font: Font {
size: 12
name: "Arial Bold"
}
content: "Start"
},
]
};
var stop = Group {
content: [
Rectangle {
x: 80
y: 290
width: 75
height: 25
opacity: 0.6
arcHeight:5
arcWidth:5
fill: LinearGradient {
startX: 0.0
startY: 0.0
endX: 0.0
endY: 1.0
proportional: true
stops: [
Stop {
offset: 0.0
color: Color.LIME
},
Stop {
offset: 0.5
color: Color.DARKGREEN
},
Stop {
offset: 1.0
color: Color.LIME
}
]
}
onMousePressed: function(e) {
tt.stop();
}
},
Text {
fill: Color.WHITE
x: 100
y: 307
font: Font {
size: 12
name: "Arial Bold"
}
content: " Stop"
},
]
}
var reset_b = Group {
content: [
Rectangle {
x: 158
y: 290
width: 79
height: 25
opacity: 0.6
arcHeight:5
arcWidth:5
fill: LinearGradient {
startX: 0.0
startY: 0.0
endX: 0.0
endY: 1.0
proportional: true
stops: [
Stop {
offset: 0.0
color: Color.LIME },
Stop {
offset: 0.5
color: Color.DARKGREEN },
Stop {
offset: 1.0
color: Color.LIME }
]
}
onMousePressed: function(e) {
tt.stop();
reset(cOdd);
reset(cEven);
count = 0;
}
},
Text {
fill: Color.WHITE
x: 175
y: 307
font: Font {
size: 12
name: "Arial Bold"
}
content: " Reset"
}
]
}
var ss = Stage {
title: "Conway's Game of Life"
style: StageStyle.TRANSPARENT
scene: Scene {
width: screenWidth
height: screenHeight
fill: Color.BLACK
content: bind [
start,
stop,
reset_b,
rectsCurr,
Text {
textOrigin: TextOrigin.TOP
font: Font {
size: 11
name: "Arial Bold"
}
content: "Generation = {count}, Interval = {interval}"
translateX: 30
translateY: buttonY - 30
fill: Color.WHITE
},
Text {
textOrigin: TextOrigin.TOP
font: Font {
size: 11
name: "Arial Bold"
}
content: "Select Red Circles and Press Start"
translateX: 25
translateY: buttonY - 50
fill: Color.WHITE
}
]
}
}