diff --git a/.babelrc b/.babelrc
new file mode 100644
index 0000000..86c445f
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,3 @@
+{
+ "presets": ["es2015", "react"]
+}
diff --git a/.gitignore b/.gitignore
index f967762..0fdded9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,5 @@ phpunit-report/*
/node_modules/
/src/Mine/SeekerBundle/Resources/public/js/node/
/src/Mine/SeekerBundle/Resources/public/js/src/
+
+nohup.out
diff --git a/README.md b/README.md
index 627569e..6b77adf 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,22 @@
-mine
-====
+This is a Symfony 3 project w/ React JS in standalone mode and w/ WebSocket.
-A Symfony project created on September 21, 2016, 10:11 am.
+ 0.) Must installed modules w/ npm are in package.json + to global:
+
+ $ npm install webpack -g
+
+ You will need a
+ .babelrc file w/ the presets
+ webpack.config.js - https://webpack.github.io/docs/webpack-for-browserify-users.html
+ same as dir where the package.json!!
+
+ (!) Tutorial: https://egghead.io/lessons/react-introduction-to-properties
+
+
+
+ 1.) Backend WebSocket server start as daemon - GeniusesOfSymfony/WebSocketBundle
+
+ $ nohup bin/console gos:websocket:server &
+
+ 2.) React JS WebPack watch generator w/ babel presets: es2015
+
+ $ webpack --progress --colors --watch
diff --git a/app/AppKernel.php b/app/AppKernel.php
index 9f3d8a6..e26e942 100644
--- a/app/AppKernel.php
+++ b/app/AppKernel.php
@@ -19,6 +19,8 @@ class AppKernel extends Kernel
new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(),
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
new FOS\UserBundle\FOSUserBundle(),
+ new Gos\Bundle\WebSocketBundle\GosWebSocketBundle(),
+ new Gos\Bundle\PubSubRouterBundle\GosPubSubRouterBundle(),
new Jotunheimr\AdminBundle\JotunheimrAdminBundle(),
new Jotunheimr\UserBundle\JotunheimrUserBundle(),
diff --git a/app/config/config.yml b/app/config/config.yml
index 4787fc0..48d8fcc 100644
--- a/app/config/config.yml
+++ b/app/config/config.yml
@@ -74,3 +74,9 @@ fos_user:
db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
firewall_name: main
user_class: Jotunheimr\UserBundle\Entity\User
+
+# Web Socket Configuration
+gos_web_socket:
+ server:
+ port: 8080 #The port the socket server will listen on
+ host: 127.0.0.1 #The host ip to bind to
diff --git a/package.json b/package.json
index efbb05a..84c2278 100644
--- a/package.json
+++ b/package.json
@@ -2,17 +2,17 @@
"name": "mine-seeker",
"version": "1.0.0",
"description": "Mine Seeker Game by system7",
- "main": "index.js",
"directories": {
"test": "tests"
},
"dependencies": {
"babel-core": "^6.14.0",
"babel-loader": "^6.2.5",
+ "babel-preset-es2015": "^6.14.0",
"babel-preset-react": "^6.11.1",
- "babelify": "^7.3.0",
"react": "^15.3.2",
- "react-dom": "^15.3.2"
+ "react-dom": "^15.3.2",
+ "react-websocket": "^1.1.6"
},
"devDependencies": {},
"scripts": {
diff --git a/src/Mine/SeekerBundle/Resources/public/css/style.mineseeker.css b/src/Mine/SeekerBundle/Resources/public/css/style.mineseeker.css
new file mode 100644
index 0000000..34a3da8
--- /dev/null
+++ b/src/Mine/SeekerBundle/Resources/public/css/style.mineseeker.css
@@ -0,0 +1,32 @@
+#mine-wrapper,
+#mine-wrapper * {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+#mine-wrapper .grid {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ flex-wrap: wrap;
+ width: 642px;
+ border: 1px solid red;
+}
+
+#mine-wrapper .grid .field {
+ background: #fff;
+ width: 40px;
+ height: 40px;
+ border: 1px solid red;
+}
+
+#mine-wrapper .grid .field.active {
+ background: blue;
+ color: #FFFFFF;
+}
+
+#mine-wrapper .grid .field.active.mine {
+ background: red;
+ color: #FFFFFF;
+}
\ No newline at end of file
diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker.js b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker.js
new file mode 100644
index 0000000..d4dab14
--- /dev/null
+++ b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import MineSeeker from './mine-seeker/app';
+
+ReactDOM.render(
+ ,
+ document.getElementById('mine-wrapper')
+);
diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/app.js b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/app.js
new file mode 100644
index 0000000..ff61d2d
--- /dev/null
+++ b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/app.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import GridControl from './grid/grid-control';
+
+class MineSeeker extends React.Component {
+ // /** after rendering */
+ // componentDidMount() {
+ // this.connection = new WebSocket('ws://127.0.0.1:8080');
+ //
+ // this.connection.onmessage = evt => {
+ // // console.log(evt.data);
+ // };
+ // }
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default MineSeeker;
diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/grid/grid-control.js b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/grid/grid-control.js
new file mode 100644
index 0000000..6a4781d
--- /dev/null
+++ b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/grid/grid-control.js
@@ -0,0 +1,160 @@
+import React from 'react';
+import Grid from './grid';
+import GridField from './grid-field';
+import UserControl from '../user/user-control';
+
+class GridControl extends React.Component {
+ constructor() {
+ super();
+
+ var grid = new Grid();
+
+ this.state = {
+ grid: grid.state.grid,
+ updated: []
+ };
+ }
+
+ refString(row, col) {
+ return 'gridField_' + row + '_' + col;
+ }
+
+ checkMine(field, i, j) {
+ return typeof field[i] !== 'undefined' && typeof field[i][j] !== 'undefined' && field[i][j] !== 'm';
+ }
+
+ checkNeighbourItem(row, col) {
+ if (this.checkMine(this.state.grid, row, col)) {
+ var currentField = this.refs[this.refString(row, col)];
+
+ currentField.setState({
+ currentObj: this.state.grid[row][col],
+ active: true
+ });
+
+ /**
+ * ez azért kell, mert amíg nem fut le a showAppropriateFields(), addig nem updatelődik a GridField.state
+ */
+ if (
+ this.state.grid[row][col] !== 0 &&
+ this.state.updated.indexOf(this.refString(row, col)) < 0 && !currentField.state.active
+ ) {
+ this.state.updated.push(this.refString(row, col));
+ }
+
+ if (
+ this.state.grid[row][col] === 0 &&
+ this.state.updated.indexOf(this.refString(row, col)) < 0 && !currentField.state.active
+ ) {
+ this.state.updated.push(this.refString(row, col));
+
+ return {
+ row: row,
+ col: col
+ };
+ }
+ }
+ return false;
+ }
+
+ checkNeighbours(row, col) {
+ var anotherFields = [];
+
+ anotherFields.push(this.checkNeighbourItem(row - 1, col));
+ anotherFields.push(this.checkNeighbourItem(row - 1, col - 1));
+ anotherFields.push(this.checkNeighbourItem(row - 1, col + 1));
+ anotherFields.push(this.checkNeighbourItem(row, col - 1));
+ anotherFields.push(this.checkNeighbourItem(row, col + 1));
+ anotherFields.push(this.checkNeighbourItem(row + 1, col));
+ anotherFields.push(this.checkNeighbourItem(row + 1, col + 1));
+ anotherFields.push(this.checkNeighbourItem(row + 1, col - 1));
+
+ return anotherFields;
+ }
+
+ showAppropriateFields(currentField, row, col) {
+ currentField.setState({
+ currentObj: this.state.grid[row][col],
+ active: true
+ });
+
+ if (this.state.updated.indexOf(this.refString(row, col)) < 0) {
+ this.state.updated.push(this.refString(row, col));
+ }
+
+ if (this.state.grid[row][col] === 0) {
+ var neighbours = this.checkNeighbours(row, col);
+
+ neighbours
+ .filter((i) => {
+ return i !== false;
+ })
+ .forEach((element, index, array) => {
+ var currentField = this.refs[this.refString(element.row, element.col)];
+ this.showAppropriateFields(currentField, element.row, element.col);
+ });
+ }
+ }
+
+ /**
+ * Player control method
+ * @param currentObject
+ */
+ handlePlayers(currentObject) {
+ var userControl = this.refs.userControl,
+ activePlayer = userControl.state.activePlayer ? 'blue' : 'red';
+
+ if (currentObject === 'm') {
+ userControl.refs[activePlayer].setState({
+ mines: userControl.refs[activePlayer].state.mines + 1
+ });
+ } else {
+ userControl.state.activePlayer = userControl.state.activePlayer ? 0 : 1;
+ }
+ }
+
+ /**
+ * Most important event!!
+ * @param coords
+ */
+ onClick(coords) {
+ var currentField = this.refs[this.refString(coords[0], coords[1])];
+ this.showAppropriateFields(currentField, coords[0], coords[1]);
+ this.handlePlayers(this.state.grid[coords[0]][coords[1]]);
+ }
+
+ renderGrid() {
+ var grid = [];
+
+ for (var i = 0, j = this.state.grid.length; i < j; i++) {
+ for (var k = 0, l = this.state.grid[i].length; k < l; k++) {
+ grid.push(
+
+ );
+ }
+ }
+
+ return grid;
+ }
+
+ render() {
+ return (
+
+
+
+
+
+ {this.renderGrid()}
+
+
+ );
+ }
+}
+
+export default GridControl;
diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/grid/grid-field.js b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/grid/grid-field.js
new file mode 100644
index 0000000..66810bf
--- /dev/null
+++ b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/grid/grid-field.js
@@ -0,0 +1,29 @@
+import React from 'react';
+
+class GridField extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ currentObj: 'w',
+ obj: this.props.obj,
+ active: false
+ };
+ }
+
+ isActive() {
+ return 'field'
+ + (this.state.active === true ? ' active' : '')
+ + (this.state.active === true && this.state.obj === 'm' ? ' mine' : '');
+ }
+
+ render() {
+ return (
+
+ {this.state.currentObj}
+
+ );
+ }
+}
+
+export default GridField;
diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/grid/grid.js b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/grid/grid.js
new file mode 100644
index 0000000..66857c7
--- /dev/null
+++ b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/grid/grid.js
@@ -0,0 +1,101 @@
+import React from 'react';
+
+class Grid extends React.Component {
+ constructor() {
+ super();
+
+ this.state = {
+ row: 16,
+ col: 16,
+ mines: 51,
+ set: []
+ };
+
+ this.state.grid = this.numberingGrid(
+ this.createGrid(
+ this.shuffleSet(
+ this.createSet(
+ this.state.set
+ )
+ )
+ )
+ );
+ }
+
+ createSet(obj) {
+ for (var i = 0, j = this.state.row * this.state.col; i < j; i++) {
+ obj.push(
+ this.state.mines > 0
+ ? "m"
+ : "w"
+ );
+ this.state.mines--;
+ }
+
+ return obj;
+ }
+
+ shuffleSet(obj) {
+ return obj.sort(function () {
+ return Math.round(Math.random()) - .5;
+ });
+ }
+
+ createGrid(obj) {
+ var grid = [[]],
+ row = 0,
+ col = 0;
+
+ for (var i = 0, j = obj.length; i < j; i++) {
+ grid[row][col] = obj[i];
+
+ if (col === 15 && row !== 15) {
+ row++;
+ col = 0;
+ grid.push([]);
+ } else {
+ col++;
+ }
+ }
+
+ return grid;
+ }
+
+ checkMine(field, i, j) {
+ return typeof field[i] !== 'undefined' && typeof field[i][j] !== 'undefined' && field[i][j] === 'm';
+ }
+
+ isThereMine(obj, row, col) {
+ if (this.checkMine(obj, row, col)) {
+ return 1;
+ }
+ return 0;
+ }
+
+ numberingGrid(obj) {
+ var nbr = 0;
+
+ for (var i = 0; i < this.state.row; i++) {
+ for (var j = 0; j < this.state.col; j++) {
+ if (obj[i][j] === 'w') {
+ nbr = 0;
+
+ nbr += this.isThereMine(obj, i - 1, j);
+ nbr += this.isThereMine(obj, i - 1, j - 1);
+ nbr += this.isThereMine(obj, i - 1, j + 1);
+ nbr += this.isThereMine(obj, i, j - 1);
+ nbr += this.isThereMine(obj, i, j + 1);
+ nbr += this.isThereMine(obj, i + 1, j);
+ nbr += this.isThereMine(obj, i + 1, j + 1);
+ nbr += this.isThereMine(obj, i + 1, j - 1);
+
+ obj[i][j] = nbr;
+ }
+ }
+ }
+
+ return obj;
+ }
+}
+
+export default Grid;
diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/user/user-control.js b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/user/user-control.js
new file mode 100644
index 0000000..3cb5167
--- /dev/null
+++ b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/user/user-control.js
@@ -0,0 +1,28 @@
+import React from 'react';
+import User from './user';
+
+class UserControl extends React.Component {
+ constructor() {
+ super();
+
+ /**
+ * activePlayer - red: 0, blue: 1
+ * @type {{activePlayer: number, mines: number}}
+ */
+ this.state = {
+ activePlayer: 0,
+ mines: 51
+ };
+ }
+
+ render() {
+ return (
+
+
+
+
+ );
+ }
+}
+
+export default UserControl;
diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/user/user.js b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/user/user.js
new file mode 100644
index 0000000..dded393
--- /dev/null
+++ b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/user/user.js
@@ -0,0 +1,23 @@
+import React from 'react';
+
+class User extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ name: this.props.name,
+ bomb: 1,
+ mines: 0
+ }
+ }
+
+ render() {
+ return (
+
+ {this.state.name}: {this.state.mines}
+
+ );
+ }
+}
+
+export default User;
diff --git a/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig b/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig
index 3682642..78f5b48 100644
--- a/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig
+++ b/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig
@@ -1,24 +1,18 @@
{% extends '::base.html.twig' %}
+{% block stylesheets %}
+ {% stylesheets filter='cssrewrite'
+ '@MineSeekerBundle/Resources/public/css/style.mineseeker.css' %}
+
+ {% endstylesheets %}
+{% endblock %}
+
{% block body %}
-
+
{% endblock %}
{% block javascripts %}
{{ parent() }}
-
-
- {% javascripts
- '@MineSeekerBundle/Resources/public/js/node/react/dist/react.js'
- '@MineSeekerBundle/Resources/public/js/src/build/react-dom.js'%}
-
- {% endjavascripts %}
-
-
+
{% endblock %}
diff --git a/web/node b/web/node
new file mode 120000
index 0000000..61c06aa
--- /dev/null
+++ b/web/node
@@ -0,0 +1 @@
+bundles/mineseeker/js/node/
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000..8bd9b41
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,21 @@
+'use strict';
+
+module.exports = {
+ entry: './web/bundles/mineseeker/js/mine-seeker.js',
+ output: {
+ path: './web/js',
+ filename: 'index.js'
+ },
+ module: {
+ loaders: [
+ {
+ test: /\.js$/,
+ exclude: /node_modules/,
+ loader: 'babel',
+ query: {
+ presets: ['es2015', 'react']
+ }
+ }
+ ]
+ }
+};