first commit
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
#BlueJ package file
|
||||
dependency1.from=Environment
|
||||
dependency1.to=Agent
|
||||
dependency1.type=UsesDependency
|
||||
dependency2.from=Environment
|
||||
dependency2.to=EnvironmentView
|
||||
dependency2.type=UsesDependency
|
||||
dependency3.from=EnvironmentView
|
||||
dependency3.to=Environment
|
||||
dependency3.type=UsesDependency
|
||||
dependency4.from=EnvironmentView
|
||||
dependency4.to=Agent
|
||||
dependency4.type=UsesDependency
|
||||
objectbench.height=76
|
||||
objectbench.width=741
|
||||
package.editor.height=412
|
||||
package.editor.width=633
|
||||
package.editor.x=70
|
||||
package.editor.y=80
|
||||
package.numDependencies=4
|
||||
package.numTargets=3
|
||||
package.showExtends=true
|
||||
package.showUses=true
|
||||
project.charset=UTF-8
|
||||
readme.editor.height=700
|
||||
readme.editor.width=900
|
||||
readme.editor.x=428
|
||||
readme.editor.y=103
|
||||
target1.editor.height=977
|
||||
target1.editor.width=1090
|
||||
target1.editor.x=407
|
||||
target1.editor.y=30
|
||||
target1.height=50
|
||||
target1.name=Agent
|
||||
target1.naviview.expanded=false
|
||||
target1.showInterface=false
|
||||
target1.type=ClassTarget
|
||||
target1.typeParameters=
|
||||
target1.width=80
|
||||
target1.x=340
|
||||
target1.y=140
|
||||
target2.editor.height=818
|
||||
target2.editor.width=999
|
||||
target2.editor.x=415
|
||||
target2.editor.y=180
|
||||
target2.height=50
|
||||
target2.name=EnvironmentView
|
||||
target2.naviview.expanded=true
|
||||
target2.showInterface=false
|
||||
target2.type=ClassTarget
|
||||
target2.typeParameters=
|
||||
target2.width=120
|
||||
target2.x=210
|
||||
target2.y=230
|
||||
target3.editor.height=867
|
||||
target3.editor.width=912
|
||||
target3.editor.x=129
|
||||
target3.editor.y=68
|
||||
target3.height=50
|
||||
target3.name=Environment
|
||||
target3.naviview.expanded=true
|
||||
target3.showInterface=false
|
||||
target3.type=ClassTarget
|
||||
target3.typeParameters=
|
||||
target3.width=100
|
||||
target3.x=120
|
||||
target3.y=50
|
@@ -0,0 +1,14 @@
|
||||
#BlueJ class context
|
||||
comment0.params=numberOfCells
|
||||
comment0.target=Automaton(int)
|
||||
comment0.text=\n\ Create\ a\ 1D\ automaton\ consisting\ of\ the\ given\ number\ of\ cells.\n\ @param\ numberOfCells\ The\ number\ of\ cells\ in\ the\ automaton.\n
|
||||
comment1.params=
|
||||
comment1.target=void\ print()
|
||||
comment1.text=\n\ Print\ the\ current\ state\ of\ the\ automaton.\n
|
||||
comment2.params=
|
||||
comment2.target=void\ update()
|
||||
comment2.text=\n\ Update\ the\ automaton\ to\ its\ next\ state.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ reset()
|
||||
comment3.text=\n\ Reset\ the\ automaton.\n
|
||||
numComments=4
|
@@ -0,0 +1,83 @@
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Model a 1D elementary cellular automaton.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29 - version 1
|
||||
*/
|
||||
public class Automaton
|
||||
{
|
||||
// The number of cells.
|
||||
private final int numberOfCells;
|
||||
// The state of the cells.
|
||||
private int[] state;
|
||||
|
||||
/**
|
||||
* Create a 1D automaton consisting of the given number of cells.
|
||||
* @param numberOfCells The number of cells in the automaton.
|
||||
*/
|
||||
public Automaton(int numberOfCells)
|
||||
{
|
||||
this.numberOfCells = numberOfCells;
|
||||
state = new int[numberOfCells];
|
||||
// Seed the automaton with a single 'on' cell in the middle.
|
||||
state[numberOfCells / 2] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the current state of the automaton.
|
||||
*/
|
||||
public void print()
|
||||
{
|
||||
for(int cellValue : state) {
|
||||
if(cellValue == 1) {
|
||||
System.out.print("*");
|
||||
}
|
||||
else {
|
||||
System.out.print(" ");
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the automaton to its next state.
|
||||
*/
|
||||
public void update()
|
||||
{
|
||||
// Build the new state in a separate array.
|
||||
int[] nextState = new int[state.length];
|
||||
// Naively update the state of each cell
|
||||
// based on the state of its two neighbors.
|
||||
for(int i = 0; i < state.length; i++) {
|
||||
int left, center, right;
|
||||
if(i == 0) {
|
||||
left = 0;
|
||||
}
|
||||
else {
|
||||
left = state[i - 1];
|
||||
}
|
||||
center = state[i];
|
||||
if(i + 1 < state.length) {
|
||||
right = state[i + 1];
|
||||
}
|
||||
else {
|
||||
right = 0;
|
||||
}
|
||||
nextState[i] = (left + center + right) % 2;
|
||||
}
|
||||
state = nextState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the automaton.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
Arrays.fill(state, 0);
|
||||
// Seed the automaton with a single 'on' cell.
|
||||
state[numberOfCells / 2] = 1;
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
#BlueJ class context
|
||||
comment0.params=numberOfCells
|
||||
comment0.target=AutomatonController(int)
|
||||
comment0.text=\n\ Create\ an\ AutomatonController.\n\ @param\ numberOfCells\ The\ number\ of\ cells\ in\ the\ automaton.\n
|
||||
comment1.params=
|
||||
comment1.target=AutomatonController()
|
||||
comment1.text=\n\ Create\ an\ AutomatonController\ with\n\ a\ default\ number\ of\ cells.\n
|
||||
comment2.params=numSteps
|
||||
comment2.target=void\ run(int)
|
||||
comment2.text=\n\ Run\ the\ automaton\ for\ the\ given\ number\ of\ steps.\n\ @param\ numSteps\ The\ number\ of\ steps.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ step()
|
||||
comment3.text=\n\ Run\ the\ automaton\ for\ a\ single\ step.\n
|
||||
comment4.params=
|
||||
comment4.target=void\ reset()
|
||||
comment4.text=\n\ Reset\ the\ automaton.\n
|
||||
numComments=5
|
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Set up and control an elementary cellular automaton.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class AutomatonController
|
||||
{
|
||||
// The automaton.
|
||||
private Automaton auto;
|
||||
|
||||
/**
|
||||
* Create an AutomatonController.
|
||||
* @param numberOfCells The number of cells in the automaton.
|
||||
*/
|
||||
public AutomatonController(int numberOfCells)
|
||||
{
|
||||
auto = new Automaton(numberOfCells);
|
||||
auto.print();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an AutomatonController with
|
||||
* a default number of cells.
|
||||
*/
|
||||
public AutomatonController()
|
||||
{
|
||||
this(50);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the automaton for the given number of steps.
|
||||
* @param numSteps The number of steps.
|
||||
*/
|
||||
public void run(int numSteps)
|
||||
{
|
||||
for(int step = 1; step <= numSteps; step++) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the automaton for a single step.
|
||||
*/
|
||||
public void step()
|
||||
{
|
||||
auto.update();
|
||||
auto.print();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the automaton.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
auto.reset();
|
||||
auto.print();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
Project: automaton
|
||||
Aothors: David J. Barnes and Michael Kölling
|
||||
|
||||
This project is part of the material of the book
|
||||
|
||||
Objects First with Java - A Practical Introduction using BlueJ
|
||||
Sixth edition
|
||||
David J. Barnes and Michael Kölling
|
||||
Pearson Education, 2016
|
||||
|
||||
It is discussed in chapter 7.
|
@@ -0,0 +1,45 @@
|
||||
#BlueJ package file
|
||||
dependency1.from=AutomatonController
|
||||
dependency1.to=Automaton
|
||||
dependency1.type=UsesDependency
|
||||
objectbench.height=76
|
||||
objectbench.width=737
|
||||
package.editor.height=400
|
||||
package.editor.width=629
|
||||
package.editor.x=70
|
||||
package.editor.y=80
|
||||
package.numDependencies=1
|
||||
package.numTargets=2
|
||||
package.showExtends=true
|
||||
package.showUses=true
|
||||
project.charset=UTF-8
|
||||
readme.editor.height=700
|
||||
readme.editor.width=900
|
||||
readme.editor.x=53
|
||||
readme.editor.y=23
|
||||
target1.editor.height=876
|
||||
target1.editor.width=955
|
||||
target1.editor.x=132
|
||||
target1.editor.y=34
|
||||
target1.height=50
|
||||
target1.name=Automaton
|
||||
target1.naviview.expanded=true
|
||||
target1.showInterface=false
|
||||
target1.type=ClassTarget
|
||||
target1.typeParameters=
|
||||
target1.width=90
|
||||
target1.x=170
|
||||
target1.y=160
|
||||
target2.editor.height=777
|
||||
target2.editor.width=994
|
||||
target2.editor.x=65
|
||||
target2.editor.y=23
|
||||
target2.height=50
|
||||
target2.name=AutomatonController
|
||||
target2.naviview.expanded=false
|
||||
target2.showInterface=false
|
||||
target2.type=ClassTarget
|
||||
target2.typeParameters=
|
||||
target2.width=160
|
||||
target2.x=70
|
||||
target2.y=70
|
@@ -0,0 +1,14 @@
|
||||
#BlueJ class context
|
||||
comment0.params=numberOfCells
|
||||
comment0.target=Automaton(int)
|
||||
comment0.text=\n\ Create\ a\ 1D\ automaton\ consisting\ of\ the\ given\ number\ of\ cells.\n\ @param\ numberOfCells\ The\ number\ of\ cells\ in\ the\ automaton.\n
|
||||
comment1.params=
|
||||
comment1.target=void\ print()
|
||||
comment1.text=\n\ Print\ the\ current\ state\ of\ the\ automaton.\n
|
||||
comment2.params=
|
||||
comment2.target=void\ update()
|
||||
comment2.text=\n\ Update\ the\ automaton\ to\ its\ next\ state.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ reset()
|
||||
comment3.text=\n\ Reset\ the\ automaton.\n
|
||||
numComments=4
|
@@ -0,0 +1,69 @@
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Model a 1D elementary cellular automaton.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29 - version 2
|
||||
*/
|
||||
public class Automaton
|
||||
{
|
||||
// The number of cells.
|
||||
private final int numberOfCells;
|
||||
// The state of the cells.
|
||||
private int[] state;
|
||||
|
||||
/**
|
||||
* Create a 1D automaton consisting of the given number of cells.
|
||||
* @param numberOfCells The number of cells in the automaton.
|
||||
*/
|
||||
public Automaton(int numberOfCells)
|
||||
{
|
||||
this.numberOfCells = numberOfCells;
|
||||
state = new int[numberOfCells];
|
||||
// Seed the automaton with a single 'on' cell in the middle.
|
||||
state[numberOfCells / 2] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the current state of the automaton.
|
||||
*/
|
||||
public void print()
|
||||
{
|
||||
for(int cellValue : state) {
|
||||
System.out.print(cellValue == 1 ? "*" : " ");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the automaton to its next state.
|
||||
*/
|
||||
public void update()
|
||||
{
|
||||
// Build the new state in a separate array.
|
||||
int[] nextState = new int[state.length];
|
||||
|
||||
int left = 0;
|
||||
int center = state[0];
|
||||
for(int i = 0; i < state.length; i++) {
|
||||
int right = i + 1 < state.length ? state[i + 1] : 0;
|
||||
|
||||
nextState[i] = (left + center + right) % 2;
|
||||
left = center;
|
||||
center = right;
|
||||
}
|
||||
state = nextState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the automaton.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
Arrays.fill(state, 0);
|
||||
// Seed the automaton with a single 'on' cell.
|
||||
state[numberOfCells / 2] = 1;
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
#BlueJ class context
|
||||
comment0.params=numberOfCells
|
||||
comment0.target=AutomatonController(int)
|
||||
comment0.text=\n\ Create\ an\ AutomatonController.\n\ @param\ numberOfCells\ The\ number\ of\ cells\ in\ the\ automaton.\n
|
||||
comment1.params=
|
||||
comment1.target=AutomatonController()
|
||||
comment1.text=\n\ Create\ an\ AutomatonController\ with\n\ a\ default\ number\ of\ cells.\n
|
||||
comment2.params=numSteps
|
||||
comment2.target=void\ run(int)
|
||||
comment2.text=\n\ Run\ the\ automaton\ for\ the\ given\ number\ of\ steps.\n\ @param\ numSteps\ The\ number\ of\ steps.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ step()
|
||||
comment3.text=\n\ Run\ the\ automaton\ for\ a\ single\ step.\n
|
||||
comment4.params=
|
||||
comment4.target=void\ reset()
|
||||
comment4.text=\n\ Reset\ the\ automaton.\n
|
||||
numComments=5
|
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Set up and control an elementary cellular automaton.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class AutomatonController
|
||||
{
|
||||
// The automaton.
|
||||
private Automaton auto;
|
||||
|
||||
/**
|
||||
* Create an AutomatonController.
|
||||
* @param numberOfCells The number of cells in the automaton.
|
||||
*/
|
||||
public AutomatonController(int numberOfCells)
|
||||
{
|
||||
auto = new Automaton(numberOfCells);
|
||||
auto.print();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an AutomatonController with
|
||||
* a default number of cells.
|
||||
*/
|
||||
public AutomatonController()
|
||||
{
|
||||
this(50);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the automaton for the given number of steps.
|
||||
* @param numSteps The number of steps.
|
||||
*/
|
||||
public void run(int numSteps)
|
||||
{
|
||||
for(int step = 1; step <= numSteps; step++) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the automaton for a single step.
|
||||
*/
|
||||
public void step()
|
||||
{
|
||||
auto.update();
|
||||
auto.print();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the automaton.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
auto.reset();
|
||||
auto.print();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
Project: automaton
|
||||
Aothors: David J. Barnes and Michael Kölling
|
||||
|
||||
This project is part of the material of the book
|
||||
|
||||
Objects First with Java - A Practical Introduction using BlueJ
|
||||
Sixth edition
|
||||
David J. Barnes and Michael Kölling
|
||||
Pearson Education, 2016
|
||||
|
||||
It is discussed in chapter 7.
|
@@ -0,0 +1,45 @@
|
||||
#BlueJ package file
|
||||
dependency1.from=AutomatonController
|
||||
dependency1.to=Automaton
|
||||
dependency1.type=UsesDependency
|
||||
objectbench.height=76
|
||||
objectbench.width=737
|
||||
package.editor.height=400
|
||||
package.editor.width=629
|
||||
package.editor.x=70
|
||||
package.editor.y=80
|
||||
package.numDependencies=1
|
||||
package.numTargets=2
|
||||
package.showExtends=true
|
||||
package.showUses=true
|
||||
project.charset=UTF-8
|
||||
readme.editor.height=700
|
||||
readme.editor.width=900
|
||||
readme.editor.x=53
|
||||
readme.editor.y=23
|
||||
target1.editor.height=774
|
||||
target1.editor.width=867
|
||||
target1.editor.x=454
|
||||
target1.editor.y=65
|
||||
target1.height=50
|
||||
target1.name=Automaton
|
||||
target1.naviview.expanded=true
|
||||
target1.showInterface=false
|
||||
target1.type=ClassTarget
|
||||
target1.typeParameters=
|
||||
target1.width=90
|
||||
target1.x=170
|
||||
target1.y=160
|
||||
target2.editor.height=777
|
||||
target2.editor.width=994
|
||||
target2.editor.x=286
|
||||
target2.editor.y=23
|
||||
target2.height=50
|
||||
target2.name=AutomatonController
|
||||
target2.naviview.expanded=false
|
||||
target2.showInterface=false
|
||||
target2.type=ClassTarget
|
||||
target2.typeParameters=
|
||||
target2.width=160
|
||||
target2.x=70
|
||||
target2.y=70
|
@@ -0,0 +1,17 @@
|
||||
#BlueJ class context
|
||||
comment0.params=numberOfCells
|
||||
comment0.target=Automaton(int)
|
||||
comment0.text=\n\ Create\ a\ 1D\ automaton\ consisting\ of\ the\ given\ number\ of\ cells.\n\ @param\ numberOfCells\ The\ number\ of\ cells\ in\ the\ automaton.\n
|
||||
comment1.params=
|
||||
comment1.target=void\ print()
|
||||
comment1.text=\n\ Print\ the\ current\ state\ of\ the\ automaton.\n
|
||||
comment2.params=
|
||||
comment2.target=void\ update()
|
||||
comment2.text=\n\ Update\ the\ automaton\ to\ its\ next\ state.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ reset()
|
||||
comment3.text=\n\ Reset\ the\ automaton.\n
|
||||
comment4.params=left\ center\ right
|
||||
comment4.target=int\ calculateNextState(int,\ int,\ int)
|
||||
comment4.text=\n\ Calculate\ the\ next\ state\ of\ the\ center\ cell\n\ given\ current\ left,\ center\ and\ right\ cell\n\ values.\n\ This\ implements\ Wolfram\ code\ 110.\n\ @see\ https\://en.wikipedia.org/wiki/Wolfram_code\n\ @param\ left\ The\ state\ of\ the\ cell\ to\ the\ left\ of\ center.\n\ @param\ center\ The\ state\ of\ the\ center\ cell.\n\ @param\ right\ The\ state\ of\ the\ cell\ to\ the\ right\ of\ center.\n\ @return\ The\ new\ value\ of\ center\ (0\ or\ 1).\n
|
||||
numComments=5
|
@@ -0,0 +1,86 @@
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Model a 1D elementary cellular automaton.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29 - version 3
|
||||
*/
|
||||
public class Automaton
|
||||
{
|
||||
// The number of cells.
|
||||
private final int numberOfCells;
|
||||
// The state of the cells.
|
||||
private int[] state;
|
||||
|
||||
/**
|
||||
* Create a 1D automaton consisting of the given number of cells.
|
||||
* @param numberOfCells The number of cells in the automaton.
|
||||
*/
|
||||
public Automaton(int numberOfCells)
|
||||
{
|
||||
this.numberOfCells = numberOfCells;
|
||||
// Allow an extra element to avoid 'fencepost' errors.
|
||||
state = new int[numberOfCells + 1];
|
||||
// Seed the automaton with a single 'on' cell.
|
||||
state[numberOfCells / 2] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the current state of the automaton.
|
||||
*/
|
||||
public void print()
|
||||
{
|
||||
for(int cellValue : state) {
|
||||
System.out.print(cellValue == 1 ? "*" : " ");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the automaton to its next state.
|
||||
*/
|
||||
public void update()
|
||||
{
|
||||
// Build the new state in a separate array.
|
||||
int[] nextState = new int[state.length];
|
||||
// Use 0 for the non-existent value to the left of
|
||||
// the first cell.
|
||||
int left = 0;
|
||||
int center = state[0];
|
||||
for(int i = 0; i < numberOfCells; i++) {
|
||||
int right = state[i + 1];
|
||||
nextState[i] = calculateNextState(left, center, right);
|
||||
left = center;
|
||||
center = right;
|
||||
}
|
||||
state = nextState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the automaton.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
Arrays.fill(state, 0);
|
||||
// Seed the automaton with a single 'on' cell in the middle.
|
||||
state[numberOfCells / 2] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the next state of the center cell
|
||||
* given current left, center and right cell
|
||||
* values.
|
||||
* This implements Wolfram code 110.
|
||||
* @see https://en.wikipedia.org/wiki/Wolfram_code
|
||||
* @param left The state of the cell to the left of center.
|
||||
* @param center The state of the center cell.
|
||||
* @param right The state of the cell to the right of center.
|
||||
* @return The new value of center (0 or 1).
|
||||
*/
|
||||
private int calculateNextState(int left, int center, int right)
|
||||
{
|
||||
return (center + right + center * right + left * center * right) % 2;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
#BlueJ class context
|
||||
comment0.params=numberOfCells
|
||||
comment0.target=AutomatonController(int)
|
||||
comment0.text=\n\ Create\ an\ AutomatonController.\n\ @param\ numberOfCells\ The\ number\ of\ cells\ in\ the\ automaton.\n
|
||||
comment1.params=
|
||||
comment1.target=AutomatonController()
|
||||
comment1.text=\n\ Create\ an\ AutomatonController\ with\n\ a\ default\ number\ of\ cells.\n
|
||||
comment2.params=numSteps
|
||||
comment2.target=void\ run(int)
|
||||
comment2.text=\n\ Run\ the\ automaton\ for\ the\ given\ number\ of\ steps.\n\ @param\ numSteps\ The\ number\ of\ steps.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ step()
|
||||
comment3.text=\n\ Run\ the\ automaton\ for\ a\ single\ step.\n
|
||||
comment4.params=
|
||||
comment4.target=void\ reset()
|
||||
comment4.text=\n\ Reset\ the\ automaton.\n
|
||||
numComments=5
|
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Set up and control an elementary cellular automaton.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class AutomatonController
|
||||
{
|
||||
// The automaton.
|
||||
private Automaton auto;
|
||||
|
||||
/**
|
||||
* Create an AutomatonController.
|
||||
* @param numberOfCells The number of cells in the automaton.
|
||||
*/
|
||||
public AutomatonController(int numberOfCells)
|
||||
{
|
||||
auto = new Automaton(numberOfCells);
|
||||
auto.print();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an AutomatonController with
|
||||
* a default number of cells.
|
||||
*/
|
||||
public AutomatonController()
|
||||
{
|
||||
this(50);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the automaton for the given number of steps.
|
||||
* @param numSteps The number of steps.
|
||||
*/
|
||||
public void run(int numSteps)
|
||||
{
|
||||
for(int step = 1; step <= numSteps; step++) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the automaton for a single step.
|
||||
*/
|
||||
public void step()
|
||||
{
|
||||
auto.update();
|
||||
auto.print();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the automaton.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
auto.reset();
|
||||
auto.print();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
Project: automaton
|
||||
Aothors: David J. Barnes and Michael Kölling
|
||||
|
||||
This project is part of the material of the book
|
||||
|
||||
Objects First with Java - A Practical Introduction using BlueJ
|
||||
Sixth edition
|
||||
David J. Barnes and Michael Kölling
|
||||
Pearson Education, 2016
|
||||
|
||||
It is discussed in chapter 7.
|
@@ -0,0 +1,45 @@
|
||||
#BlueJ package file
|
||||
dependency1.from=AutomatonController
|
||||
dependency1.to=Automaton
|
||||
dependency1.type=UsesDependency
|
||||
objectbench.height=76
|
||||
objectbench.width=737
|
||||
package.editor.height=400
|
||||
package.editor.width=629
|
||||
package.editor.x=70
|
||||
package.editor.y=80
|
||||
package.numDependencies=1
|
||||
package.numTargets=2
|
||||
package.showExtends=true
|
||||
package.showUses=true
|
||||
project.charset=UTF-8
|
||||
readme.editor.height=700
|
||||
readme.editor.width=900
|
||||
readme.editor.x=53
|
||||
readme.editor.y=23
|
||||
target1.editor.height=769
|
||||
target1.editor.width=980
|
||||
target1.editor.x=375
|
||||
target1.editor.y=141
|
||||
target1.height=50
|
||||
target1.name=Automaton
|
||||
target1.naviview.expanded=true
|
||||
target1.showInterface=false
|
||||
target1.type=ClassTarget
|
||||
target1.typeParameters=
|
||||
target1.width=90
|
||||
target1.x=170
|
||||
target1.y=160
|
||||
target2.editor.height=777
|
||||
target2.editor.width=994
|
||||
target2.editor.x=286
|
||||
target2.editor.y=23
|
||||
target2.height=50
|
||||
target2.name=AutomatonController
|
||||
target2.naviview.expanded=false
|
||||
target2.showInterface=false
|
||||
target2.type=ClassTarget
|
||||
target2.typeParameters=
|
||||
target2.width=160
|
||||
target2.x=70
|
||||
target2.y=70
|
@@ -0,0 +1,20 @@
|
||||
#BlueJ class context
|
||||
comment0.params=numberOfCells
|
||||
comment0.target=Automaton(int)
|
||||
comment0.text=\n\ Create\ a\ 1D\ automaton\ consisting\ of\ the\ given\ number\ of\ cells.\n\ @param\ numberOfCells\ The\ number\ of\ cells\ in\ the\ automaton.\n
|
||||
comment1.params=
|
||||
comment1.target=void\ print()
|
||||
comment1.text=\n\ Print\ the\ current\ state\ of\ the\ automaton.\n
|
||||
comment2.params=
|
||||
comment2.target=void\ update()
|
||||
comment2.text=\n\ Update\ the\ automaton\ to\ its\ next\ state.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ reset()
|
||||
comment3.text=\n\ Reset\ the\ automaton.\n
|
||||
comment4.params=left\ center\ right
|
||||
comment4.target=int\ calculateNextState(int,\ int,\ int)
|
||||
comment4.text=\n\ Calculate\ the\ next\ state\ of\ the\ center\ cell\n\ given\ current\ left,\ center\ and\ right\ cell\n\ values.\n\ This\ implements\ Wolfram\ code\ 110.\n\ @see\ https\://en.wikipedia.org/wiki/Wolfram_code\n\ @param\ left\ The\ state\ of\ the\ cell\ to\ the\ left\ of\ center.\n\ @param\ center\ The\ state\ of\ the\ center\ cell.\n\ @param\ right\ The\ state\ of\ the\ cell\ to\ the\ right\ of\ center.\n\ @return\ The\ new\ value\ of\ center\ (0\ or\ 1).\n
|
||||
comment5.params=left\ center\ right
|
||||
comment5.target=int\ encodeTriplet(int,\ int,\ int)
|
||||
comment5.text=\n\ Encode\ the\ 1/0\ triplet\ (left,\ center,\ right)\ as\ an\n\ integer\ value\ in\ the\ range\ 0-7.\n\ @param\ left\ The\ state\ of\ the\ cell\ to\ the\ left\ of\ center\ (0\ or\ 1).\n\ @param\ center\ The\ state\ of\ the\ center\ cell\ (0\ or\ 1).\n\ @param\ right\ The\ state\ of\ the\ cell\ to\ the\ right\ of\ center\ (0\ or\ 1).\n\ @return\ (left,center,right)\ interpreted\ as\ a\ 3-bit\ value.\n
|
||||
numComments=6
|
@@ -0,0 +1,104 @@
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Model a 1D elementary cellular automaton.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29 - version 4
|
||||
*/
|
||||
public class Automaton
|
||||
{
|
||||
// The number of cells.
|
||||
private final int numberOfCells;
|
||||
// The state of the cells.
|
||||
private int[] state;
|
||||
// The state table encoding the next-state changes.
|
||||
private int[] stateTable;
|
||||
|
||||
/**
|
||||
* Create a 1D automaton consisting of the given number of cells.
|
||||
* @param numberOfCells The number of cells in the automaton.
|
||||
*/
|
||||
public Automaton(int numberOfCells)
|
||||
{
|
||||
this.numberOfCells = numberOfCells;
|
||||
// Allow an extra element to avoid 'fencepost' errors.
|
||||
state = new int[numberOfCells + 1];
|
||||
stateTable = new int[] {
|
||||
0, 1, 0, 0, 1, 0, 0, 1, // Wolfram code 146
|
||||
};
|
||||
// Seed the automaton with a single 'on' cell.
|
||||
state[numberOfCells / 2] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the current state of the automaton.
|
||||
*/
|
||||
public void print()
|
||||
{
|
||||
for(int cellValue : state) {
|
||||
System.out.print(cellValue == 1 ? "*" : " ");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the automaton to its next state.
|
||||
*/
|
||||
public void update()
|
||||
{
|
||||
// Build the new state in a separate array.
|
||||
int[] nextState = new int[state.length];
|
||||
// Use 0 for the non-existent value to the left of
|
||||
// the first cell.
|
||||
int left = 0;
|
||||
int center = state[0];
|
||||
for(int i = 0; i < numberOfCells; i++) {
|
||||
int right = state[i + 1];
|
||||
nextState[i] = calculateNextState(left, center, right);
|
||||
left = center;
|
||||
center = right;
|
||||
}
|
||||
state = nextState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the automaton.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
Arrays.fill(state, 0);
|
||||
// Seed the automaton with a single 'on' cell in the middle.
|
||||
state[numberOfCells / 2] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the next state of the center cell
|
||||
* given current left, center and right cell
|
||||
* values.
|
||||
* This implements Wolfram code 110.
|
||||
* @see https://en.wikipedia.org/wiki/Wolfram_code
|
||||
* @param left The state of the cell to the left of center.
|
||||
* @param center The state of the center cell.
|
||||
* @param right The state of the cell to the right of center.
|
||||
* @return The new value of center (0 or 1).
|
||||
*/
|
||||
private int calculateNextState(int left, int center, int right)
|
||||
{
|
||||
return stateTable[encodeTriplet(left, center, right)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the 1/0 triplet (left, center, right) as an
|
||||
* integer value in the range 0-7.
|
||||
* @param left The state of the cell to the left of center (0 or 1).
|
||||
* @param center The state of the center cell (0 or 1).
|
||||
* @param right The state of the cell to the right of center (0 or 1).
|
||||
* @return (left,center,right) interpreted as a 3-bit value.
|
||||
*/
|
||||
private int encodeTriplet(int left, int center, int right)
|
||||
{
|
||||
return left * 4 + center * 2 + right;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
#BlueJ class context
|
||||
comment0.params=numberOfCells
|
||||
comment0.target=AutomatonController(int)
|
||||
comment0.text=\n\ Create\ an\ AutomatonController.\n\ @param\ numberOfCells\ The\ number\ of\ cells\ in\ the\ automaton.\n
|
||||
comment1.params=
|
||||
comment1.target=AutomatonController()
|
||||
comment1.text=\n\ Create\ an\ AutomatonController\ with\n\ a\ default\ number\ of\ cells.\n
|
||||
comment2.params=numSteps
|
||||
comment2.target=void\ run(int)
|
||||
comment2.text=\n\ Run\ the\ automaton\ for\ the\ given\ number\ of\ steps.\n\ @param\ numSteps\ The\ number\ of\ steps.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ step()
|
||||
comment3.text=\n\ Run\ the\ automaton\ for\ a\ single\ step.\n
|
||||
comment4.params=
|
||||
comment4.target=void\ reset()
|
||||
comment4.text=\n\ Reset\ the\ automaton.\n
|
||||
numComments=5
|
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Set up and control an elementary cellular automaton.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class AutomatonController
|
||||
{
|
||||
// The automaton.
|
||||
private Automaton auto;
|
||||
|
||||
/**
|
||||
* Create an AutomatonController.
|
||||
* @param numberOfCells The number of cells in the automaton.
|
||||
*/
|
||||
public AutomatonController(int numberOfCells)
|
||||
{
|
||||
auto = new Automaton(numberOfCells);
|
||||
auto.print();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an AutomatonController with
|
||||
* a default number of cells.
|
||||
*/
|
||||
public AutomatonController()
|
||||
{
|
||||
this(50);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the automaton for the given number of steps.
|
||||
* @param numSteps The number of steps.
|
||||
*/
|
||||
public void run(int numSteps)
|
||||
{
|
||||
for(int step = 1; step <= numSteps; step++) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the automaton for a single step.
|
||||
*/
|
||||
public void step()
|
||||
{
|
||||
auto.update();
|
||||
auto.print();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the automaton.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
auto.reset();
|
||||
auto.print();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
Project: automaton
|
||||
Aothors: David J. Barnes and Michael Kölling
|
||||
|
||||
This project is part of the material of the book
|
||||
|
||||
Objects First with Java - A Practical Introduction using BlueJ
|
||||
Sixth edition
|
||||
David J. Barnes and Michael Kölling
|
||||
Pearson Education, 2016
|
||||
|
||||
It is discussed in chapter 7.
|
@@ -0,0 +1,45 @@
|
||||
#BlueJ package file
|
||||
dependency1.from=AutomatonController
|
||||
dependency1.to=Automaton
|
||||
dependency1.type=UsesDependency
|
||||
objectbench.height=76
|
||||
objectbench.width=737
|
||||
package.editor.height=400
|
||||
package.editor.width=629
|
||||
package.editor.x=70
|
||||
package.editor.y=80
|
||||
package.numDependencies=1
|
||||
package.numTargets=2
|
||||
package.showExtends=true
|
||||
package.showUses=true
|
||||
project.charset=UTF-8
|
||||
readme.editor.height=700
|
||||
readme.editor.width=900
|
||||
readme.editor.x=53
|
||||
readme.editor.y=23
|
||||
target1.editor.height=720
|
||||
target1.editor.width=938
|
||||
target1.editor.x=449
|
||||
target1.editor.y=137
|
||||
target1.height=50
|
||||
target1.name=Automaton
|
||||
target1.naviview.expanded=true
|
||||
target1.showInterface=false
|
||||
target1.type=ClassTarget
|
||||
target1.typeParameters=
|
||||
target1.width=90
|
||||
target1.x=170
|
||||
target1.y=160
|
||||
target2.editor.height=777
|
||||
target2.editor.width=994
|
||||
target2.editor.x=286
|
||||
target2.editor.y=23
|
||||
target2.height=50
|
||||
target2.name=AutomatonController
|
||||
target2.naviview.expanded=false
|
||||
target2.showInterface=false
|
||||
target2.type=ClassTarget
|
||||
target2.typeParameters=
|
||||
target2.width=160
|
||||
target2.x=70
|
||||
target2.y=70
|
@@ -0,0 +1,20 @@
|
||||
#BlueJ class context
|
||||
comment0.params=
|
||||
comment0.target=Cell()
|
||||
comment0.text=\n\ Set\ the\ initial\ state\ to\ be\ DEAD.\n
|
||||
comment1.params=initialState
|
||||
comment1.target=Cell(int)
|
||||
comment1.text=\n\ Set\ the\ initial\ state.\n\ @param\ initialState\ The\ initial\ state\n
|
||||
comment2.params=
|
||||
comment2.target=int\ getNextState()
|
||||
comment2.text=\n\ Determine\ this\ cell's\ next\ state,\ based\ on\ the\n\ state\ of\ its\ neighbors.\n\ This\ is\ an\ implementation\ of\ the\ rules\ for\ Brian's\ Brain.\n\ @return\ The\ next\ state.\n
|
||||
comment3.params=neighborList
|
||||
comment3.target=void\ setNeighbors(java.util.ArrayList)
|
||||
comment3.text=\n\ Receive\ the\ list\ of\ neighboring\ cells\ and\ take\n\ a\ copy.\n\ @param\ neighborList\ Neighboring\ cells.\n
|
||||
comment4.params=
|
||||
comment4.target=int\ getState()
|
||||
comment4.text=\n\ Get\ the\ state\ of\ this\ cell.\n\ @return\ The\ state.\n
|
||||
comment5.params=state
|
||||
comment5.target=void\ setState(int)
|
||||
comment5.text=\n\ Set\ the\ state\ of\ this\ cell.\n\ @param\ The\ state.\n
|
||||
numComments=6
|
@@ -0,0 +1,97 @@
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A cell in a 2D cellular automaton.
|
||||
* The cell has multiple possible states.
|
||||
* This is an implementation of the rules for Brian's Brain.
|
||||
* @see https://en.wikipedia.org/wiki/Brian%27s_Brain
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class Cell
|
||||
{
|
||||
// The possible states.
|
||||
public static final int ALIVE = 0, DEAD = 1, DYING = 2;
|
||||
// The number of possible states.
|
||||
public static final int NUM_STATES = 3;
|
||||
|
||||
// The cell's state.
|
||||
private int state;
|
||||
// The cell's neighbors.
|
||||
private Cell[] neighbors;
|
||||
|
||||
/**
|
||||
* Set the initial state to be DEAD.
|
||||
*/
|
||||
public Cell()
|
||||
{
|
||||
this(DEAD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the initial state.
|
||||
* @param initialState The initial state
|
||||
*/
|
||||
public Cell(int initialState)
|
||||
{
|
||||
state = initialState;
|
||||
neighbors = new Cell[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine this cell's next state, based on the
|
||||
* state of its neighbors.
|
||||
* This is an implementation of the rules for Brian's Brain.
|
||||
* @return The next state.
|
||||
*/
|
||||
public int getNextState()
|
||||
{
|
||||
if(state == DEAD) {
|
||||
// Count the number of neighbors that are alive.
|
||||
int aliveCount = 0;
|
||||
for(Cell n : neighbors) {
|
||||
if(n.getState() == ALIVE) {
|
||||
aliveCount++;
|
||||
}
|
||||
}
|
||||
return aliveCount == 2 ? ALIVE : DEAD;
|
||||
}
|
||||
else if(state == DYING) {
|
||||
return DEAD;
|
||||
}
|
||||
else {
|
||||
return DYING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the list of neighboring cells and take
|
||||
* a copy.
|
||||
* @param neighborList Neighboring cells.
|
||||
*/
|
||||
public void setNeighbors(ArrayList<Cell> neighborList)
|
||||
{
|
||||
neighbors = new Cell[neighborList.size()];
|
||||
neighborList.toArray(neighbors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state of this cell.
|
||||
* @return The state.
|
||||
*/
|
||||
public int getState()
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of this cell.
|
||||
* @param The state.
|
||||
*/
|
||||
public void setState(int state)
|
||||
{
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
#BlueJ class context
|
||||
comment0.params=
|
||||
comment0.target=Environment()
|
||||
comment0.text=\n\ Create\ an\ environment\ with\ the\ default\ size.\n
|
||||
comment1.params=numRows\ numCols
|
||||
comment1.target=Environment(int,\ int)
|
||||
comment1.text=\n\ Create\ an\ environment\ with\ the\ given\ size.\n\ @param\ numRows\ The\ number\ of\ rows.\n\ @param\ numCols\ The\ number\ of\ cols;\n
|
||||
comment2.params=
|
||||
comment2.target=void\ step()
|
||||
comment2.text=\n\ Run\ the\ automaton\ for\ one\ step.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ reset()
|
||||
comment3.text=\n\ Reset\ the\ state\ of\ the\ automaton\ to\ all\ DEAD.\n
|
||||
comment4.params=
|
||||
comment4.target=void\ randomize()
|
||||
comment4.text=\n\ Generate\ a\ random\ setup.\n
|
||||
comment5.params=row\ col\ state
|
||||
comment5.target=void\ setCellState(int,\ int,\ int)
|
||||
comment5.text=\n\ Set\ the\ state\ of\ one\ cell.\n\ @param\ row\ The\ cell's\ row.\n\ @param\ col\ The\ cell's\ col.\n\ @param\ state\ The\ cell's\ state.\n
|
||||
comment6.params=
|
||||
comment6.target=Cell[][]\ getCells()
|
||||
comment6.text=\n\ Return\ the\ grid\ of\ cells.\n\ @return\ The\ grid\ of\ cells.\n
|
||||
comment7.params=numRows\ numCols
|
||||
comment7.target=void\ setup(int,\ int)
|
||||
comment7.text=\n\ Setup\ a\ new\ environment\ of\ the\ given\ size.\n\ @param\ numRows\ The\ number\ of\ rows.\n\ @param\ numCols\ The\ number\ of\ cols;\n
|
||||
comment8.params=
|
||||
comment8.target=void\ setupNeighbors()
|
||||
comment8.text=\n\ Give\ to\ a\ cell\ a\ list\ of\ its\ neighbors.\n
|
||||
numComments=9
|
@@ -0,0 +1,161 @@
|
||||
import java.security.SecureRandom;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Maintain the environment for a 2D cellular automaton.
|
||||
*
|
||||
* @author David J. Barnes
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class Environment
|
||||
{
|
||||
// Default size for the environment.
|
||||
private static final int DEFAULT_ROWS = 50;
|
||||
private static final int DEFAULT_COLS = 50;
|
||||
|
||||
// The grid of cells.
|
||||
private Cell[][] cells;
|
||||
// Visualization of the environment.
|
||||
private final EnvironmentView view;
|
||||
|
||||
/**
|
||||
* Create an environment with the default size.
|
||||
*/
|
||||
public Environment()
|
||||
{
|
||||
this(DEFAULT_ROWS, DEFAULT_COLS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an environment with the given size.
|
||||
* @param numRows The number of rows.
|
||||
* @param numCols The number of cols;
|
||||
*/
|
||||
public Environment(int numRows, int numCols)
|
||||
{
|
||||
setup(numRows, numCols);
|
||||
randomize();
|
||||
view = new EnvironmentView(this, numRows, numCols);
|
||||
view.showCells();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the automaton for one step.
|
||||
*/
|
||||
public void step()
|
||||
{
|
||||
int numRows = cells.length;
|
||||
int numCols = cells[0].length;
|
||||
// Build a record of the next state of each cell.
|
||||
int[][] nextStates = new int[numRows][numCols];
|
||||
// Ask each cell to determine its next state.
|
||||
for(int row = 0; row < numRows; row++) {
|
||||
int[] rowOfStates = nextStates[row];
|
||||
for(int col = 0; col < numCols; col++) {
|
||||
rowOfStates[col] = cells[row][col].getNextState();
|
||||
}
|
||||
}
|
||||
// Update the cells' states.
|
||||
for(int row = 0; row < numRows; row++) {
|
||||
int[] rowOfStates = nextStates[row];
|
||||
for(int col = 0; col < numCols; col++) {
|
||||
setCellState(row, col, rowOfStates[col]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the state of the automaton to all DEAD.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
int numRows = cells.length;
|
||||
int numCols = cells[0].length;
|
||||
for(int row = 0; row < numRows; row++) {
|
||||
for(int col = 0; col < numCols; col++) {
|
||||
setCellState(row, col, Cell.DEAD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random setup.
|
||||
*/
|
||||
public void randomize()
|
||||
{
|
||||
int numRows = cells.length;
|
||||
int numCols = cells[0].length;
|
||||
SecureRandom rand = new SecureRandom();
|
||||
for(int row = 0; row < numRows; row++) {
|
||||
for(int col = 0; col < numCols; col++) {
|
||||
setCellState(row, col, rand.nextInt(Cell.NUM_STATES));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of one cell.
|
||||
* @param row The cell's row.
|
||||
* @param col The cell's col.
|
||||
* @param state The cell's state.
|
||||
*/
|
||||
public void setCellState(int row, int col, int state)
|
||||
{
|
||||
cells[row][col].setState(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the grid of cells.
|
||||
* @return The grid of cells.
|
||||
*/
|
||||
public Cell[][] getCells()
|
||||
{
|
||||
return cells;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a new environment of the given size.
|
||||
* @param numRows The number of rows.
|
||||
* @param numCols The number of cols;
|
||||
*/
|
||||
private void setup(int numRows, int numCols)
|
||||
{
|
||||
cells = new Cell[numRows][numCols];
|
||||
for(int row = 0; row < numRows; row++) {
|
||||
for (int col = 0; col < numCols; col++) {
|
||||
cells[row][col] = new Cell();
|
||||
}
|
||||
}
|
||||
setupNeighbors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Give to a cell a list of its neighbors.
|
||||
*/
|
||||
private void setupNeighbors()
|
||||
{
|
||||
int numRows = cells.length;
|
||||
int numCols = cells[0].length;
|
||||
// Allow for 8 neighbors plus the cell.
|
||||
ArrayList<Cell> neighbors = new ArrayList<>(9);
|
||||
for(int row = 0; row < numRows; row++) {
|
||||
for(int col = 0; col < numCols; col++) {
|
||||
Cell cell = cells[row][col];
|
||||
// This process will also include the cell.
|
||||
for(int dr = -1; dr <= 1; dr++) {
|
||||
for(int dc = -1; dc <= 1; dc++) {
|
||||
int nr = (numRows + row + dr) % numRows;
|
||||
int nc = (numCols + col + dc) % numCols;
|
||||
neighbors.add(cells[nr][nc]);
|
||||
}
|
||||
}
|
||||
// The neighbours should not include the cell at
|
||||
// (row,col) so remove it.
|
||||
neighbors.remove(cell);
|
||||
cell.setNeighbors(neighbors);
|
||||
neighbors.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
#BlueJ class context
|
||||
comment0.params=env\ rows\ cols
|
||||
comment0.target=EnvironmentView(Environment,\ int,\ int)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ EnvironmentView\n\ @param\ env\n
|
||||
comment1.params=rows\ cols
|
||||
comment1.target=void\ setupGrid(int,\ int)
|
||||
comment1.text=\n\ Setup\ a\ new\ environment\ of\ the\ given\ size.\n\ @param\ rows\ The\ number\ of\ rows.\n\ @param\ cols\ The\ number\ of\ cols;\n
|
||||
comment2.params=
|
||||
comment2.target=void\ showCells()
|
||||
comment2.text=\n\ Show\ the\ states\ of\ the\ cells.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ setupControls()
|
||||
comment3.text=\n\ Set\ up\ the\ animation\ controls.\n
|
||||
comment4.params=speedPercentage
|
||||
comment4.target=void\ setDelay(int)
|
||||
comment4.text=\n\ Set\ the\ animation\ delay.\n\ @param\ speedPercentage\ (100-speedPercentage)\ as\ a\ percentage\ of\ the\ LONGEST_DELAY.\n
|
||||
numComments=5
|
@@ -0,0 +1,271 @@
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
/**
|
||||
* A GUI for the environment, with runtime controls.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class EnvironmentView extends JFrame
|
||||
{
|
||||
// The longest delay for the animation, in milliseconds.
|
||||
private static final int LONGEST_DELAY = 1000;
|
||||
// Colors for the different cell states.
|
||||
private static final Color[] colors = {
|
||||
Color.WHITE, // Alive
|
||||
new Color(68, 100, 129), // Dead
|
||||
new Color(204, 196, 72), // Dying
|
||||
};
|
||||
|
||||
private GridView view;
|
||||
private final Environment env;
|
||||
private boolean running;
|
||||
private int delay;
|
||||
|
||||
/**
|
||||
* Constructor for objects of class EnvironmentView
|
||||
* @param env
|
||||
*/
|
||||
public EnvironmentView(Environment env, int rows, int cols)
|
||||
{
|
||||
super("2D Cellular Automaton");
|
||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
setLocation(20, 20);
|
||||
this.env = env;
|
||||
this.running = false;
|
||||
setDelay(50);
|
||||
setupControls();
|
||||
setupGrid(rows, cols);
|
||||
pack();
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a new environment of the given size.
|
||||
* @param rows The number of rows.
|
||||
* @param cols The number of cols;
|
||||
*/
|
||||
private void setupGrid(int rows, int cols)
|
||||
{
|
||||
Container contents = getContentPane();
|
||||
view = new GridView(rows, cols);
|
||||
contents.add(view, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the states of the cells.
|
||||
*/
|
||||
public void showCells()
|
||||
{
|
||||
Cell[][] cells = env.getCells();
|
||||
if(!isVisible()) {
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
view.preparePaint();
|
||||
for(int row = 0; row < cells.length; row++) {
|
||||
Cell[] cellRow = cells[row];
|
||||
int numCols = cellRow.length;
|
||||
for(int col = 0; col < numCols; col++) {
|
||||
int state = cellRow[col].getState();
|
||||
view.drawMark(col, row, colors[state]);
|
||||
}
|
||||
}
|
||||
|
||||
view.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the animation controls.
|
||||
*/
|
||||
private void setupControls()
|
||||
{
|
||||
// Continuous running.
|
||||
final JButton run = new JButton("Run");
|
||||
run.addActionListener(e -> {
|
||||
if(!running) {
|
||||
running = true;
|
||||
try {
|
||||
new Runner().execute();
|
||||
}
|
||||
catch(Exception ex) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Single stepping.
|
||||
final JButton step = new JButton("Step");
|
||||
step.addActionListener(e -> {
|
||||
running = false;
|
||||
env.step();
|
||||
showCells();
|
||||
});
|
||||
|
||||
// Pause the animation.
|
||||
final JButton pause = new JButton("Pause");
|
||||
pause.addActionListener(e -> running = false);
|
||||
|
||||
// Reset of the environment
|
||||
final JButton reset = new JButton("Reset");
|
||||
reset.addActionListener(e -> {
|
||||
running = false;
|
||||
env.reset();
|
||||
showCells();
|
||||
});
|
||||
|
||||
// Randomize the environment.
|
||||
final JButton randomize = new JButton("Random");
|
||||
randomize.addActionListener(e -> {
|
||||
running = false;
|
||||
env.randomize();
|
||||
showCells();
|
||||
});
|
||||
|
||||
Container contents = getContentPane();
|
||||
|
||||
// A speed controller.
|
||||
final JSlider speedSlider = new JSlider(0, 100);
|
||||
speedSlider.addChangeListener(e -> {
|
||||
setDelay(speedSlider.getValue());
|
||||
});
|
||||
Container speedPanel = new JPanel();
|
||||
speedPanel.setLayout(new GridLayout(2, 1));
|
||||
speedPanel.add(new JLabel("Animation Speed", SwingConstants.CENTER));
|
||||
speedPanel.add(speedSlider);
|
||||
contents.add(speedPanel, BorderLayout.NORTH);
|
||||
|
||||
// Place the button controls.
|
||||
JPanel controls = new JPanel();
|
||||
controls.add(run);
|
||||
controls.add(step);
|
||||
controls.add(pause);
|
||||
controls.add(reset);
|
||||
controls.add(randomize);
|
||||
|
||||
contents.add(controls, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the animation delay.
|
||||
* @param speedPercentage (100-speedPercentage) as a percentage of the LONGEST_DELAY.
|
||||
*/
|
||||
private void setDelay(int speedPercentage)
|
||||
{
|
||||
delay = (int) ((100.0 - speedPercentage) * LONGEST_DELAY / 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide stepping of the animation.
|
||||
*/
|
||||
private class Runner extends SwingWorker<Boolean, Void>
|
||||
{
|
||||
@Override
|
||||
/**
|
||||
* Repeatedly single-step the environment as long
|
||||
* as the animation is running.
|
||||
*/
|
||||
public Boolean doInBackground()
|
||||
{
|
||||
while(running) {
|
||||
env.step();
|
||||
showCells();
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
}
|
||||
catch(InterruptedException e) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provide a graphical view of a rectangular grid.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private class GridView extends JPanel
|
||||
{
|
||||
private final int GRID_VIEW_SCALING_FACTOR = 10;
|
||||
|
||||
private final int gridWidth, gridHeight;
|
||||
private int xScale, yScale;
|
||||
private Dimension size;
|
||||
private Graphics g;
|
||||
private Image fieldImage;
|
||||
|
||||
/**
|
||||
* Create a new GridView component.
|
||||
*/
|
||||
public GridView(int height, int width)
|
||||
{
|
||||
gridHeight = height;
|
||||
gridWidth = width;
|
||||
size = new Dimension(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the GUI manager how big we would like to be.
|
||||
*/
|
||||
@Override
|
||||
public Dimension getPreferredSize()
|
||||
{
|
||||
return new Dimension(gridWidth * GRID_VIEW_SCALING_FACTOR,
|
||||
gridHeight * GRID_VIEW_SCALING_FACTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for a new round of painting. Since the component
|
||||
* may be resized, compute the scaling factor again.
|
||||
*/
|
||||
public void preparePaint()
|
||||
{
|
||||
if(! size.equals(getSize())) {
|
||||
size = getSize();
|
||||
fieldImage = view.createImage(size.width, size.height);
|
||||
g = fieldImage.getGraphics();
|
||||
|
||||
xScale = size.width / gridWidth;
|
||||
if(xScale < 1) {
|
||||
xScale = GRID_VIEW_SCALING_FACTOR;
|
||||
}
|
||||
yScale = size.height / gridHeight;
|
||||
if(yScale < 1) {
|
||||
yScale = GRID_VIEW_SCALING_FACTOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paint on grid location on this field in a given color.
|
||||
*/
|
||||
public void drawMark(int x, int y, Color color)
|
||||
{
|
||||
g.setColor(color);
|
||||
g.fillRect(x * xScale, y * yScale, xScale-1, yScale-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The field view component needs to be redisplayed. Copy the
|
||||
* internal image to screen.
|
||||
*/
|
||||
@Override
|
||||
public void paintComponent(Graphics g)
|
||||
{
|
||||
if(fieldImage != null) {
|
||||
Dimension currentSize = getSize();
|
||||
if(size.equals(currentSize)) {
|
||||
g.drawImage(fieldImage, 0, 0, null);
|
||||
}
|
||||
else {
|
||||
// Rescale the previous image.
|
||||
g.drawImage(fieldImage, 0, 0, currentSize.width, currentSize.height, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
Project: brain
|
||||
Aothors: David J. Barnes and Michael Kölling
|
||||
|
||||
This project is part of the material of the book
|
||||
|
||||
Objects First with Java - A Practical Introduction using BlueJ
|
||||
Sixth edition
|
||||
David J. Barnes and Michael Kölling
|
||||
Pearson Education, 2016
|
||||
|
||||
It is discussed in chapter 7.
|
||||
|
||||
An implementation of the 2D cellular automaton known as Brian's Brain.
|
||||
See https://en.wikipedia.org/wiki/Brian%27s_Brain
|
||||
|
@@ -0,0 +1,67 @@
|
||||
#BlueJ package file
|
||||
dependency1.from=Environment
|
||||
dependency1.to=Cell
|
||||
dependency1.type=UsesDependency
|
||||
dependency2.from=Environment
|
||||
dependency2.to=EnvironmentView
|
||||
dependency2.type=UsesDependency
|
||||
dependency3.from=EnvironmentView
|
||||
dependency3.to=Environment
|
||||
dependency3.type=UsesDependency
|
||||
dependency4.from=EnvironmentView
|
||||
dependency4.to=Cell
|
||||
dependency4.type=UsesDependency
|
||||
objectbench.height=76
|
||||
objectbench.width=741
|
||||
package.editor.height=412
|
||||
package.editor.width=633
|
||||
package.editor.x=70
|
||||
package.editor.y=80
|
||||
package.numDependencies=4
|
||||
package.numTargets=3
|
||||
package.showExtends=true
|
||||
package.showUses=true
|
||||
project.charset=UTF-8
|
||||
readme.editor.height=700
|
||||
readme.editor.width=900
|
||||
readme.editor.x=53
|
||||
readme.editor.y=23
|
||||
target1.editor.height=818
|
||||
target1.editor.width=999
|
||||
target1.editor.x=798
|
||||
target1.editor.y=92
|
||||
target1.height=50
|
||||
target1.name=EnvironmentView
|
||||
target1.naviview.expanded=false
|
||||
target1.showInterface=false
|
||||
target1.type=ClassTarget
|
||||
target1.typeParameters=
|
||||
target1.width=120
|
||||
target1.x=210
|
||||
target1.y=230
|
||||
target2.editor.height=945
|
||||
target2.editor.width=954
|
||||
target2.editor.x=206
|
||||
target2.editor.y=214
|
||||
target2.height=50
|
||||
target2.name=Environment
|
||||
target2.naviview.expanded=true
|
||||
target2.showInterface=false
|
||||
target2.type=ClassTarget
|
||||
target2.typeParameters=
|
||||
target2.width=100
|
||||
target2.x=120
|
||||
target2.y=50
|
||||
target3.editor.height=777
|
||||
target3.editor.width=969
|
||||
target3.editor.x=88
|
||||
target3.editor.y=23
|
||||
target3.height=50
|
||||
target3.name=Cell
|
||||
target3.naviview.expanded=true
|
||||
target3.showInterface=false
|
||||
target3.type=ClassTarget
|
||||
target3.typeParameters=
|
||||
target3.width=80
|
||||
target3.x=340
|
||||
target3.y=140
|
@@ -0,0 +1,14 @@
|
||||
#BlueJ class context
|
||||
comment0.params=
|
||||
comment0.target=LogAnalyzer()
|
||||
comment0.text=\n\ Create\ an\ object\ to\ analyze\ hourly\ web\ accesses.\n
|
||||
comment1.params=
|
||||
comment1.target=void\ analyzeHourlyData()
|
||||
comment1.text=\n\ Analyze\ the\ hourly\ access\ data\ from\ the\ log\ file.\n
|
||||
comment2.params=
|
||||
comment2.target=void\ printHourlyCounts()
|
||||
comment2.text=\n\ Print\ the\ hourly\ counts.\n\ These\ should\ have\ been\ set\ with\ a\ prior\n\ call\ to\ analyzeHourlyData.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ printData()
|
||||
comment3.text=\n\ Print\ the\ lines\ of\ data\ read\ by\ the\ LogfileReader\n
|
||||
numComments=4
|
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Read web server data and analyse hourly access patterns.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling.
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class LogAnalyzer
|
||||
{
|
||||
// Where to calculate the hourly access counts.
|
||||
private int[] hourCounts;
|
||||
// Use a LogfileReader to access the data.
|
||||
private LogfileReader reader;
|
||||
|
||||
/**
|
||||
* Create an object to analyze hourly web accesses.
|
||||
*/
|
||||
public LogAnalyzer()
|
||||
{
|
||||
// Create the array object to hold the hourly
|
||||
// access counts.
|
||||
hourCounts = new int[24];
|
||||
// Create the reader to obtain the data.
|
||||
reader = new LogfileReader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze the hourly access data from the log file.
|
||||
*/
|
||||
public void analyzeHourlyData()
|
||||
{
|
||||
while(reader.hasNext()) {
|
||||
LogEntry entry = reader.next();
|
||||
int hour = entry.getHour();
|
||||
hourCounts[hour]++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the hourly counts.
|
||||
* These should have been set with a prior
|
||||
* call to analyzeHourlyData.
|
||||
*/
|
||||
public void printHourlyCounts()
|
||||
{
|
||||
System.out.println("Hr: Count");
|
||||
for(int hour = 0; hour < hourCounts.length; hour++) {
|
||||
System.out.println(hour + ": " + hourCounts[hour]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the lines of data read by the LogfileReader
|
||||
*/
|
||||
public void printData()
|
||||
{
|
||||
reader.printData();
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
#BlueJ class context
|
||||
comment0.params=logline
|
||||
comment0.target=LogEntry(java.lang.String)
|
||||
comment0.text=\n\ Decompose\ a\ log\ line\ so\ that\ the\ individual\ fields\n\ are\ available.\n\ @param\ logline\ A\ single\ line\ from\ the\ log.\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ This\ should\ be\ in\ the\ format\:\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ year\ month\ day\ hour\ minute\ etc.\n
|
||||
comment1.params=year\ month\ day\ hour\ minute
|
||||
comment1.target=LogEntry(int,\ int,\ int,\ int,\ int)
|
||||
comment1.text=\n\ Create\ a\ LogEntry\ from\ the\ individual\ components.\n\ @param\ year\ The\ year\n\ @param\ month\ The\ month\ (1-12)\n\ @param\ day\ The\ day\ (1-31)\n\ @param\ hour\ The\ hour\ (0-23)\n\ @param\ minute\ The\ minute\ (0-59)\n
|
||||
comment2.params=
|
||||
comment2.target=int\ getHour()
|
||||
comment2.text=\n\ Return\ the\ hour.\n\ @return\ The\ hour\ field\ from\ the\ log\ line.\n
|
||||
comment3.params=
|
||||
comment3.target=int\ getMinute()
|
||||
comment3.text=\n\ Return\ the\ minute.\n\ @return\ The\ minute\ field\ from\ the\ log\ line.\n
|
||||
comment4.params=
|
||||
comment4.target=java.lang.String\ toString()
|
||||
comment4.text=\n\ Create\ a\ string\ representation\ of\ the\ data.\n\ This\ is\ not\ necessarily\ identical\ with\ the\n\ text\ of\ the\ original\ log\ line.\n\ @return\ A\ string\ representing\ the\ data\ of\ this\ entry.\n
|
||||
comment5.params=otherEntry
|
||||
comment5.target=int\ compareTo(LogEntry)
|
||||
comment5.text=\n\ Compare\ the\ date/time\ combination\ of\ this\ log\ entry\n\ with\ another.\n\ @param\ otherEntry\ The\ other\ entry\ to\ compare\ against.\n\ @return\ A\ negative\ value\ if\ this\ entry\ comes\ before\ the\ other.\n\ \ \ \ \ \ \ \ \ A\ positive\ value\ if\ this\ entry\ comes\ after\ the\ other.\n\ \ \ \ \ \ \ \ \ Zero\ if\ the\ entries\ are\ the\ same.\n
|
||||
comment6.params=
|
||||
comment6.target=java.util.Calendar\ getWhen()
|
||||
comment6.text=\n\ Return\ the\ Calendar\ object\ representing\ this\ event.\n\ @return\ The\ Calendar\ for\ this\ event.\n
|
||||
comment7.params=
|
||||
comment7.target=void\ setWhen()
|
||||
comment7.text=\n\ Create\ an\ equivalent\ Calendar\ object\ from\ the\ data\ values.\n
|
||||
numComments=8
|
@@ -0,0 +1,140 @@
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Store the data from a single line of a
|
||||
* web-server log file.
|
||||
* Individual fields are made available via
|
||||
* accessors such as getHour() and getMinute().
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling.
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class LogEntry implements Comparable<LogEntry>
|
||||
{
|
||||
// Where the data values extracted from a single
|
||||
// log line are stored.
|
||||
private int[] dataValues;
|
||||
// The equivalent Calendar object for the log time.
|
||||
private Calendar when;
|
||||
|
||||
// At which index in dataValues the different fields
|
||||
// from a log line are stored.
|
||||
private static final int YEAR = 0, MONTH = 1, DAY = 2,
|
||||
HOUR = 3, MINUTE = 4;
|
||||
// The number of fields. If more fields are added, e.g. for
|
||||
// seconds or a status code, then this value must be increased
|
||||
// to match.
|
||||
private static final int NUMBER_OF_FIELDS = 5;
|
||||
|
||||
/**
|
||||
* Decompose a log line so that the individual fields
|
||||
* are available.
|
||||
* @param logline A single line from the log.
|
||||
* This should be in the format:
|
||||
* year month day hour minute etc.
|
||||
*/
|
||||
public LogEntry(String logline)
|
||||
{
|
||||
// The array to store the data for a single line.
|
||||
dataValues = new int[NUMBER_OF_FIELDS];
|
||||
// Break up the log line.
|
||||
LoglineTokenizer tokenizer = new LoglineTokenizer();
|
||||
tokenizer.tokenize(logline,dataValues);
|
||||
setWhen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LogEntry from the individual components.
|
||||
* @param year The year
|
||||
* @param month The month (1-12)
|
||||
* @param day The day (1-31)
|
||||
* @param hour The hour (0-23)
|
||||
* @param minute The minute (0-59)
|
||||
*/
|
||||
public LogEntry(int year, int month, int day, int hour, int minute)
|
||||
{
|
||||
// The array to store the data for a single line.
|
||||
dataValues = new int[NUMBER_OF_FIELDS];
|
||||
dataValues[YEAR] = year;
|
||||
dataValues[MONTH] = month;
|
||||
dataValues[DAY] = day;
|
||||
dataValues[HOUR] = hour;
|
||||
dataValues[MINUTE] = minute;
|
||||
setWhen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hour.
|
||||
* @return The hour field from the log line.
|
||||
*/
|
||||
public int getHour()
|
||||
{
|
||||
return dataValues[HOUR];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minute.
|
||||
* @return The minute field from the log line.
|
||||
*/
|
||||
public int getMinute()
|
||||
{
|
||||
return dataValues[MINUTE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string representation of the data.
|
||||
* This is not necessarily identical with the
|
||||
* text of the original log line.
|
||||
* @return A string representing the data of this entry.
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for(int value : dataValues) {
|
||||
// Prefix a leading zero on single digit numbers.
|
||||
if(value < 10) {
|
||||
buffer.append('0');
|
||||
}
|
||||
buffer.append(value);
|
||||
buffer.append(' ');
|
||||
}
|
||||
// Drop any trailing space.
|
||||
return buffer.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the date/time combination of this log entry
|
||||
* with another.
|
||||
* @param otherEntry The other entry to compare against.
|
||||
* @return A negative value if this entry comes before the other.
|
||||
* A positive value if this entry comes after the other.
|
||||
* Zero if the entries are the same.
|
||||
*/
|
||||
public int compareTo(LogEntry otherEntry)
|
||||
{
|
||||
// Use the equivalent Calendars comparison method.
|
||||
return when.compareTo(otherEntry.getWhen());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Calendar object representing this event.
|
||||
* @return The Calendar for this event.
|
||||
*/
|
||||
private Calendar getWhen()
|
||||
{
|
||||
return when;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an equivalent Calendar object from the data values.
|
||||
*/
|
||||
private void setWhen()
|
||||
{
|
||||
when = Calendar.getInstance();
|
||||
// Adjust from 1-based month and day to 0-based.
|
||||
when.set(dataValues[YEAR],
|
||||
dataValues[MONTH] - 1, dataValues[DAY] - 1,
|
||||
dataValues[HOUR], dataValues[MINUTE]);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
#BlueJ class context
|
||||
comment0.params=
|
||||
comment0.target=LogfileCreator()
|
||||
comment0.text=\n\ Create\ log\ files.\n
|
||||
comment1.params=filename\ numEntries
|
||||
comment1.target=boolean\ createFile(java.lang.String,\ int)
|
||||
comment1.text=\n\ Create\ a\ file\ of\ random\ log\ entries.\n\ @param\ filename\ The\ file\ to\ write.\n\ @param\ numEntries\ How\ many\ entries.\n\ @return\ true\ if\ successful,\ false\ otherwise.\n
|
||||
comment2.params=
|
||||
comment2.target=LogEntry\ createEntry()
|
||||
comment2.text=\n\ Create\ a\ single\ (random)\ entry\ for\ a\ log\ file.\n\ @return\ A\ log\ entry\ containing\ random\ data.\n
|
||||
numComments=3
|
@@ -0,0 +1,69 @@
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A class for creating log files of random data.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class LogfileCreator
|
||||
{
|
||||
private Random rand;
|
||||
|
||||
/**
|
||||
* Create log files.
|
||||
*/
|
||||
public LogfileCreator()
|
||||
{
|
||||
rand = new Random();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file of random log entries.
|
||||
* @param filename The file to write.
|
||||
* @param numEntries How many entries.
|
||||
* @return true if successful, false otherwise.
|
||||
*/
|
||||
public boolean createFile(String filename, int numEntries)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
if(numEntries > 0) {
|
||||
try (FileWriter writer = new FileWriter(filename)) {
|
||||
LogEntry[] entries = new LogEntry[numEntries];
|
||||
for(int i = 0; i < numEntries; i++) {
|
||||
entries[i] = createEntry();
|
||||
}
|
||||
Arrays.sort(entries);
|
||||
for(int i = 0; i < numEntries; i++) {
|
||||
writer.write(entries[i].toString());
|
||||
writer.write('\n');
|
||||
}
|
||||
|
||||
success = true;
|
||||
}
|
||||
catch(IOException e) {
|
||||
System.err.println("There was a problem writing to " + filename);
|
||||
}
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single (random) entry for a log file.
|
||||
* @return A log entry containing random data.
|
||||
*/
|
||||
public LogEntry createEntry()
|
||||
{
|
||||
int year = 2016;
|
||||
int month = 1 + rand.nextInt(12);
|
||||
// Avoid the complexities of days-per-month.
|
||||
int day = 1 + rand.nextInt(28);
|
||||
int hour = rand.nextInt(24);
|
||||
int minute = rand.nextInt(60);
|
||||
return new LogEntry(year, month, day, hour, minute);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
#BlueJ class context
|
||||
comment0.params=
|
||||
comment0.target=LogfileReader()
|
||||
comment0.text=\n\ Create\ a\ LogfileReader\ to\ supply\ data\ from\ a\ default\ file.\n
|
||||
comment1.params=filename
|
||||
comment1.target=LogfileReader(java.lang.String)
|
||||
comment1.text=\n\ Create\ a\ LogfileReader\ that\ will\ supply\ data\n\ from\ a\ particular\ log\ file.\ \n\ @param\ filename\ The\ file\ of\ log\ data.\n
|
||||
comment2.params=
|
||||
comment2.target=boolean\ hasNext()
|
||||
comment2.text=\n\ Does\ the\ reader\ have\ more\ data\ to\ supply?\n\ @return\ true\ if\ there\ is\ more\ data\ available,\n\ \ \ \ \ \ \ \ \ false\ otherwise.\n
|
||||
comment3.params=
|
||||
comment3.target=LogEntry\ next()
|
||||
comment3.text=\n\ Analyze\ the\ next\ line\ from\ the\ log\ file\ and\n\ make\ it\ available\ via\ a\ LogEntry\ object.\n\ \n\ @return\ A\ LogEntry\ containing\ the\ data\ from\ the\n\ \ \ \ \ \ \ \ \ next\ log\ line.\n
|
||||
comment4.params=
|
||||
comment4.target=void\ remove()
|
||||
comment4.text=\n\ Remove\ an\ entry.\n\ This\ operation\ is\ not\ permitted.\n
|
||||
comment5.params=
|
||||
comment5.target=java.lang.String\ getFormat()
|
||||
comment5.text=\n\ @return\ A\ string\ explaining\ the\ format\ of\ the\ data\n\ \ \ \ \ \ \ \ \ in\ the\ log\ file.\n
|
||||
comment6.params=
|
||||
comment6.target=void\ reset()
|
||||
comment6.text=\n\ Set\ up\ a\ fresh\ iterator\ to\ provide\ access\ to\ the\ data.\n\ This\ allows\ a\ single\ file\ of\ data\ to\ be\ processed\n\ more\ than\ once.\n
|
||||
comment7.params=
|
||||
comment7.target=void\ printData()
|
||||
comment7.text=\n\ Print\ the\ data.\n
|
||||
comment8.params=data
|
||||
comment8.target=void\ createSimulatedData(java.util.ArrayList)
|
||||
comment8.text=\n\ Provide\ a\ sample\ of\ simulated\ data.\n\ NB\:\ To\ simplify\ the\ creation\ of\ this\ data,\ no\n\ days\ after\ the\ 28th\ of\ a\ month\ are\ ever\ generated.\n\ @param\ data\ Where\ to\ store\ the\ simulated\ LogEntry\ objects.\n
|
||||
numComments=9
|
@@ -0,0 +1,161 @@
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* A class to read information from a file of web server accesses.
|
||||
* Currently, the log file is assumed to contain simply
|
||||
* date and time information in the format:
|
||||
*
|
||||
* year month day hour minute
|
||||
* Log entries are sorted into ascending order of date.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling.
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class LogfileReader implements Iterator<LogEntry>
|
||||
{
|
||||
// The data format in the log file.
|
||||
private String format;
|
||||
// Where the file's contents are stored in the form
|
||||
// of LogEntry objects.
|
||||
private ArrayList<LogEntry> entries;
|
||||
// An iterator over entries.
|
||||
private Iterator<LogEntry> dataIterator;
|
||||
|
||||
/**
|
||||
* Create a LogfileReader to supply data from a default file.
|
||||
*/
|
||||
public LogfileReader()
|
||||
{
|
||||
this("weblog.txt");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LogfileReader that will supply data
|
||||
* from a particular log file.
|
||||
* @param filename The file of log data.
|
||||
*/
|
||||
public LogfileReader(String filename)
|
||||
{
|
||||
// The format for the data.
|
||||
format = "Year Month(1-12) Day Hour Minute";
|
||||
// Where to store the data.
|
||||
entries = new ArrayList<>();
|
||||
|
||||
// Attempt to read the complete set of data from file.
|
||||
boolean dataRead;
|
||||
try{
|
||||
// Locate the file with respect to the current environment.
|
||||
URL fileURL = getClass().getClassLoader().getResource(filename);
|
||||
if(fileURL == null) {
|
||||
throw new FileNotFoundException(filename);
|
||||
}
|
||||
Scanner logfile = new Scanner(new File(fileURL.toURI()));
|
||||
// Read the data lines until the end of file.
|
||||
while(logfile.hasNextLine()) {
|
||||
String logline = logfile.nextLine();
|
||||
// Break up the line and add it to the list of entries.
|
||||
LogEntry entry = new LogEntry(logline);
|
||||
entries.add(entry);
|
||||
}
|
||||
logfile.close();
|
||||
dataRead = true;
|
||||
}
|
||||
catch(FileNotFoundException | URISyntaxException e) {
|
||||
System.out.println("Problem encountered: " + e);
|
||||
dataRead = false;
|
||||
}
|
||||
// If we couldn't read the log file, use simulated data.
|
||||
if(!dataRead) {
|
||||
System.out.println("Failed to read the data file: " + filename);
|
||||
System.out.println("Using simulated data instead.");
|
||||
createSimulatedData(entries);
|
||||
}
|
||||
// Sort the entries into ascending order.
|
||||
Collections.sort(entries);
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the reader have more data to supply?
|
||||
* @return true if there is more data available,
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean hasNext()
|
||||
{
|
||||
return dataIterator.hasNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze the next line from the log file and
|
||||
* make it available via a LogEntry object.
|
||||
*
|
||||
* @return A LogEntry containing the data from the
|
||||
* next log line.
|
||||
*/
|
||||
public LogEntry next()
|
||||
{
|
||||
return dataIterator.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an entry.
|
||||
* This operation is not permitted.
|
||||
*/
|
||||
public void remove()
|
||||
{
|
||||
System.err.println("It is not permitted to remove entries.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A string explaining the format of the data
|
||||
* in the log file.
|
||||
*/
|
||||
public String getFormat()
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a fresh iterator to provide access to the data.
|
||||
* This allows a single file of data to be processed
|
||||
* more than once.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
dataIterator = entries.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the data.
|
||||
*/
|
||||
public void printData()
|
||||
{
|
||||
for(LogEntry entry : entries) {
|
||||
System.out.println(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a sample of simulated data.
|
||||
* NB: To simplify the creation of this data, no
|
||||
* days after the 28th of a month are ever generated.
|
||||
* @param data Where to store the simulated LogEntry objects.
|
||||
*/
|
||||
private void createSimulatedData(ArrayList<LogEntry> data)
|
||||
{
|
||||
LogfileCreator creator = new LogfileCreator();
|
||||
// How many simulated entries we want.
|
||||
int numEntries = 100;
|
||||
for(int i = 0; i < numEntries; i++) {
|
||||
data.add(creator.createEntry());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
#BlueJ class context
|
||||
comment0.params=
|
||||
comment0.target=LoglineTokenizer()
|
||||
comment0.text=\n\ Construct\ a\ LogLineAnalyzer\n
|
||||
comment1.params=logline\ dataLine
|
||||
comment1.target=void\ tokenize(java.lang.String,\ int[])
|
||||
comment1.text=\n\ Tokenize\ a\ log\ line.\ Place\ the\ integer\ values\ from\n\ it\ into\ an\ array.\ The\ number\ of\ tokens\ on\ the\ line\n\ must\ be\ sufficient\ to\ fill\ the\ array.\n\n\ @param\ logline\ The\ line\ to\ be\ tokenized.\n\ @param\ dataLine\ Where\ to\ store\ the\ values.\n
|
||||
numComments=2
|
@@ -0,0 +1,43 @@
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Break up line from a web server log file into
|
||||
* its separate fields.
|
||||
* Currently, the log file is assumed to contain simply
|
||||
* integer date and time information.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kolling.
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class LoglineTokenizer
|
||||
{
|
||||
/**
|
||||
* Construct a LogLineAnalyzer
|
||||
*/
|
||||
public LoglineTokenizer()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenize a log line. Place the integer values from
|
||||
* it into an array. The number of tokens on the line
|
||||
* must be sufficient to fill the array.
|
||||
*
|
||||
* @param logline The line to be tokenized.
|
||||
* @param dataLine Where to store the values.
|
||||
*/
|
||||
public void tokenize(String logline, int[] dataLine)
|
||||
{
|
||||
try {
|
||||
// Scan the logline for integers.
|
||||
Scanner tokenizer = new Scanner(logline);
|
||||
for(int i = 0; i < dataLine.length; i++) {
|
||||
dataLine[i] = tokenizer.nextInt();
|
||||
}
|
||||
}
|
||||
catch(java.util.NoSuchElementException e) {
|
||||
System.out.println("Insuffient data items on log line: " + logline);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
Project: weblog-analyzer
|
||||
Aothors: David J. Barnes and Michael Kölling
|
||||
|
||||
This project is part of the material for chapter 7 of the book
|
||||
|
||||
Objects First with Java - A Practical Introduction using BlueJ
|
||||
Sixth edition
|
||||
David J. Barnes and Michael Kölling
|
||||
Pearson Education, 2016
|
||||
|
||||
|
||||
Purpose of project: To provide an illustration of the use of arrays.
|
||||
How to start this project: Create a LogAnalyzer object.
|
||||
|
||||
The LogfileReader expects to read a file, weblog.txt,
|
||||
containing lines of data in the format:
|
||||
|
||||
year month day hour minute
|
||||
|
||||
month values are in the range 1-12 and day values in the range 1-31.
|
||||
If the sample file cannot be found, the reader will create some simulated
|
||||
data. Alternatively, use the LogfileCreator to create some random data.
|
||||
Use its createFile method to give a file name and the number of entries
|
||||
to create.
|
@@ -0,0 +1,99 @@
|
||||
#BlueJ package file
|
||||
dependency1.from=LogAnalyzer
|
||||
dependency1.to=LogfileReader
|
||||
dependency1.type=UsesDependency
|
||||
dependency2.from=LogAnalyzer
|
||||
dependency2.to=LogEntry
|
||||
dependency2.type=UsesDependency
|
||||
dependency3.from=LogfileReader
|
||||
dependency3.to=LogEntry
|
||||
dependency3.type=UsesDependency
|
||||
dependency4.from=LogfileCreator
|
||||
dependency4.to=LogEntry
|
||||
dependency4.type=UsesDependency
|
||||
dependency5.from=LogEntry
|
||||
dependency5.to=LoglineTokenizer
|
||||
dependency5.type=UsesDependency
|
||||
dependency6.from=LogfileReader
|
||||
dependency6.to=LogfileCreator
|
||||
dependency6.type=UsesDependency
|
||||
objectbench.height=76
|
||||
objectbench.width=859
|
||||
package.editor.height=479
|
||||
package.editor.width=751
|
||||
package.editor.x=70
|
||||
package.editor.y=80
|
||||
package.numDependencies=6
|
||||
package.numTargets=5
|
||||
package.showExtends=true
|
||||
package.showUses=true
|
||||
project.charset=UTF-8
|
||||
readme.editor.height=707
|
||||
readme.editor.width=830
|
||||
readme.editor.x=151
|
||||
readme.editor.y=60
|
||||
target1.editor.height=892
|
||||
target1.editor.width=1119
|
||||
target1.editor.x=149
|
||||
target1.editor.y=86
|
||||
target1.height=60
|
||||
target1.name=LogfileCreator
|
||||
target1.naviview.expanded=true
|
||||
target1.showInterface=false
|
||||
target1.type=ClassTarget
|
||||
target1.typeParameters=
|
||||
target1.width=120
|
||||
target1.x=500
|
||||
target1.y=60
|
||||
target2.editor.height=784
|
||||
target2.editor.width=986
|
||||
target2.editor.x=131
|
||||
target2.editor.y=292
|
||||
target2.height=60
|
||||
target2.name=LogfileReader
|
||||
target2.naviview.expanded=true
|
||||
target2.showInterface=false
|
||||
target2.type=ClassTarget
|
||||
target2.typeParameters=
|
||||
target2.width=110
|
||||
target2.x=230
|
||||
target2.y=140
|
||||
target3.editor.height=700
|
||||
target3.editor.width=900
|
||||
target3.editor.x=53
|
||||
target3.editor.y=23
|
||||
target3.height=60
|
||||
target3.name=LoglineTokenizer
|
||||
target3.naviview.expanded=false
|
||||
target3.showInterface=false
|
||||
target3.type=ClassTarget
|
||||
target3.typeParameters=
|
||||
target3.width=130
|
||||
target3.x=490
|
||||
target3.y=320
|
||||
target4.editor.height=621
|
||||
target4.editor.width=853
|
||||
target4.editor.x=54
|
||||
target4.editor.y=23
|
||||
target4.height=60
|
||||
target4.name=LogAnalyzer
|
||||
target4.naviview.expanded=true
|
||||
target4.showInterface=false
|
||||
target4.type=ClassTarget
|
||||
target4.typeParameters=
|
||||
target4.width=120
|
||||
target4.x=80
|
||||
target4.y=60
|
||||
target5.editor.height=800
|
||||
target5.editor.width=989
|
||||
target5.editor.x=53
|
||||
target5.editor.y=23
|
||||
target5.height=60
|
||||
target5.name=LogEntry
|
||||
target5.naviview.expanded=true
|
||||
target5.showInterface=false
|
||||
target5.type=ClassTarget
|
||||
target5.typeParameters=
|
||||
target5.width=110
|
||||
target5.x=370
|
||||
target5.y=230
|
3749
Semester 1/Programming 1/Java/examples/projects/chapter07/weblog-analyzer/weblog.txt
Executable file
3749
Semester 1/Programming 1/Java/examples/projects/chapter07/weblog-analyzer/weblog.txt
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user